|
use super::{
|
|
DefaultMerkleStore as MerkleStore, EmptySubtreeRoots, MerkleError, MerklePath, NodeIndex,
|
|
PartialMerkleTree, RecordingMerkleStore, Rpo256, RpoDigest,
|
|
};
|
|
use crate::{
|
|
merkle::{digests_to_words, int_to_leaf, int_to_node, MerkleTree, SimpleSmt},
|
|
Felt, Word, ONE, WORD_SIZE, ZERO,
|
|
};
|
|
|
|
#[cfg(feature = "std")]
|
|
use super::{Deserializable, Serializable};
|
|
|
|
#[cfg(feature = "std")]
|
|
use std::error::Error;
|
|
|
|
// TEST DATA
|
|
// ================================================================================================
|
|
|
|
const KEYS4: [u64; 4] = [0, 1, 2, 3];
|
|
const VALUES4: [RpoDigest; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
|
|
|
|
const KEYS8: [u64; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
|
|
const VALUES8: [RpoDigest; 8] = [
|
|
int_to_node(1),
|
|
int_to_node(2),
|
|
int_to_node(3),
|
|
int_to_node(4),
|
|
int_to_node(5),
|
|
int_to_node(6),
|
|
int_to_node(7),
|
|
int_to_node(8),
|
|
];
|
|
|
|
// TESTS
|
|
// ================================================================================================
|
|
|
|
#[test]
|
|
fn test_root_not_in_store() -> Result<(), MerkleError> {
|
|
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
|
|
let store = MerkleStore::from(&mtree);
|
|
assert_eq!(
|
|
store.get_node(VALUES4[0], NodeIndex::make(mtree.depth(), 0)),
|
|
Err(MerkleError::RootNotInStore(VALUES4[0])),
|
|
"Leaf 0 is not a root"
|
|
);
|
|
assert_eq!(
|
|
store.get_path(VALUES4[0], NodeIndex::make(mtree.depth(), 0)),
|
|
Err(MerkleError::RootNotInStore(VALUES4[0])),
|
|
"Leaf 0 is not a root"
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_merkle_tree() -> Result<(), MerkleError> {
|
|
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
|
|
let store = MerkleStore::from(&mtree);
|
|
|
|
// STORE LEAVES ARE CORRECT -------------------------------------------------------------------
|
|
// checks the leaves in the store corresponds to the expected values
|
|
assert_eq!(
|
|
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 0)),
|
|
Ok(VALUES4[0]),
|
|
"node 0 must be in the tree"
|
|
);
|
|
assert_eq!(
|
|
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 1)),
|
|
Ok(VALUES4[1]),
|
|
"node 1 must be in the tree"
|
|
);
|
|
assert_eq!(
|
|
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 2)),
|
|
Ok(VALUES4[2]),
|
|
"node 2 must be in the tree"
|
|
);
|
|
assert_eq!(
|
|
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 3)),
|
|
Ok(VALUES4[3]),
|
|
"node 3 must be in the tree"
|
|
);
|
|
|
|
// STORE LEAVES MATCH TREE --------------------------------------------------------------------
|
|
// sanity check the values returned by the store and the tree
|
|
assert_eq!(
|
|
mtree.get_node(NodeIndex::make(mtree.depth(), 0)),
|
|
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 0)),
|
|
"node 0 must be the same for both MerkleTree and MerkleStore"
|
|
);
|
|
assert_eq!(
|
|
mtree.get_node(NodeIndex::make(mtree.depth(), 1)),
|
|
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 1)),
|
|
"node 1 must be the same for both MerkleTree and MerkleStore"
|
|
);
|
|
assert_eq!(
|
|
mtree.get_node(NodeIndex::make(mtree.depth(), 2)),
|
|
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 2)),
|
|
"node 2 must be the same for both MerkleTree and MerkleStore"
|
|
);
|
|
assert_eq!(
|
|
mtree.get_node(NodeIndex::make(mtree.depth(), 3)),
|
|
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 3)),
|
|
"node 3 must be the same for both MerkleTree and MerkleStore"
|
|
);
|
|
|
|
// STORE MERKLE PATH MATCHS ==============================================================
|
|
// assert the merkle path returned by the store is the same as the one in the tree
|
|
let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 0)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[0], result.value,
|
|
"Value for merkle path at index 0 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
mtree.get_path(NodeIndex::make(mtree.depth(), 0)),
|
|
Ok(result.path),
|
|
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 1)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[1], result.value,
|
|
"Value for merkle path at index 0 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
mtree.get_path(NodeIndex::make(mtree.depth(), 1)),
|
|
Ok(result.path),
|
|
"merkle path for index 1 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 2)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[2], result.value,
|
|
"Value for merkle path at index 0 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
mtree.get_path(NodeIndex::make(mtree.depth(), 2)),
|
|
Ok(result.path),
|
|
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 3)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[3], result.value,
|
|
"Value for merkle path at index 0 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
mtree.get_path(NodeIndex::make(mtree.depth(), 3)),
|
|
Ok(result.path),
|
|
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_empty_roots() {
|
|
let store = MerkleStore::default();
|
|
let mut root = RpoDigest::default();
|
|
|
|
for depth in 0..255 {
|
|
root = Rpo256::merge(&[root; 2]);
|
|
assert!(
|
|
store.get_node(root, NodeIndex::make(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::make(depth, 0);
|
|
let store_path = store.get_path(smt.root(), index)?;
|
|
let smt_path = smt.get_path(index)?;
|
|
assert_eq!(
|
|
store_path.value,
|
|
RpoDigest::default(),
|
|
"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(), RpoDigest::default()).unwrap(),
|
|
smt.root(),
|
|
"computed root from the path must match the empty tree root"
|
|
);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_invalid_node() {
|
|
let mtree =
|
|
MerkleTree::new(digests_to_words(&VALUES4)).expect("creating a merkle tree must work");
|
|
let store = MerkleStore::from(&mtree);
|
|
let _ = store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 3));
|
|
}
|
|
|
|
#[test]
|
|
fn test_add_sparse_merkle_tree_one_level() -> Result<(), MerkleError> {
|
|
let keys2: [u64; 2] = [0, 1];
|
|
let leaves2: [Word; 2] = [int_to_leaf(1), int_to_leaf(2)];
|
|
let smt = SimpleSmt::with_leaves(1, keys2.into_iter().zip(leaves2)).unwrap();
|
|
let store = MerkleStore::from(&smt);
|
|
|
|
let idx = NodeIndex::make(1, 0);
|
|
assert_eq!(smt.get_node(idx).unwrap(), leaves2[0].into());
|
|
assert_eq!(store.get_node(smt.root(), idx).unwrap(), smt.get_node(idx).unwrap());
|
|
|
|
let idx = NodeIndex::make(1, 1);
|
|
assert_eq!(smt.get_node(idx).unwrap(), leaves2[1].into());
|
|
assert_eq!(store.get_node(smt.root(), idx).unwrap(), smt.get_node(idx).unwrap());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
|
|
let smt = SimpleSmt::with_leaves(
|
|
SimpleSmt::MAX_DEPTH,
|
|
KEYS4.into_iter().zip(digests_to_words(&VALUES4)),
|
|
)
|
|
.unwrap();
|
|
|
|
let store = MerkleStore::from(&smt);
|
|
|
|
// STORE LEAVES ARE CORRECT ==============================================================
|
|
// checks the leaves in the store corresponds to the expected values
|
|
assert_eq!(
|
|
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 0)),
|
|
Ok(VALUES4[0]),
|
|
"node 0 must be in the tree"
|
|
);
|
|
assert_eq!(
|
|
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 1)),
|
|
Ok(VALUES4[1]),
|
|
"node 1 must be in the tree"
|
|
);
|
|
assert_eq!(
|
|
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 2)),
|
|
Ok(VALUES4[2]),
|
|
"node 2 must be in the tree"
|
|
);
|
|
assert_eq!(
|
|
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 3)),
|
|
Ok(VALUES4[3]),
|
|
"node 3 must be in the tree"
|
|
);
|
|
assert_eq!(
|
|
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 4)),
|
|
Ok(RpoDigest::default()),
|
|
"unmodified node 4 must be ZERO"
|
|
);
|
|
|
|
// STORE LEAVES MATCH TREE ===============================================================
|
|
// sanity check the values returned by the store and the tree
|
|
assert_eq!(
|
|
smt.get_node(NodeIndex::make(smt.depth(), 0)),
|
|
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 0)),
|
|
"node 0 must be the same for both SparseMerkleTree and MerkleStore"
|
|
);
|
|
assert_eq!(
|
|
smt.get_node(NodeIndex::make(smt.depth(), 1)),
|
|
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 1)),
|
|
"node 1 must be the same for both SparseMerkleTree and MerkleStore"
|
|
);
|
|
assert_eq!(
|
|
smt.get_node(NodeIndex::make(smt.depth(), 2)),
|
|
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 2)),
|
|
"node 2 must be the same for both SparseMerkleTree and MerkleStore"
|
|
);
|
|
assert_eq!(
|
|
smt.get_node(NodeIndex::make(smt.depth(), 3)),
|
|
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 3)),
|
|
"node 3 must be the same for both SparseMerkleTree and MerkleStore"
|
|
);
|
|
assert_eq!(
|
|
smt.get_node(NodeIndex::make(smt.depth(), 4)),
|
|
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 4)),
|
|
"node 4 must be the same for both SparseMerkleTree and MerkleStore"
|
|
);
|
|
|
|
// STORE MERKLE PATH MATCHS ==============================================================
|
|
// assert the merkle path returned by the store is the same as the one in the tree
|
|
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 0)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[0], result.value,
|
|
"Value for merkle path at index 0 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
smt.get_path(NodeIndex::make(smt.depth(), 0)),
|
|
Ok(result.path),
|
|
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 1)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[1], result.value,
|
|
"Value for merkle path at index 1 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
smt.get_path(NodeIndex::make(smt.depth(), 1)),
|
|
Ok(result.path),
|
|
"merkle path for index 1 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 2)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[2], result.value,
|
|
"Value for merkle path at index 2 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
smt.get_path(NodeIndex::make(smt.depth(), 2)),
|
|
Ok(result.path),
|
|
"merkle path for index 2 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 3)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[3], result.value,
|
|
"Value for merkle path at index 3 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
smt.get_path(NodeIndex::make(smt.depth(), 3)),
|
|
Ok(result.path),
|
|
"merkle path for index 3 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 4)).unwrap();
|
|
assert_eq!(
|
|
RpoDigest::default(),
|
|
result.value,
|
|
"Value for merkle path at index 4 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
smt.get_path(NodeIndex::make(smt.depth(), 4)),
|
|
Ok(result.path),
|
|
"merkle path for index 4 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_add_merkle_paths() -> Result<(), MerkleError> {
|
|
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
|
|
|
|
let i0 = 0;
|
|
let p0 = mtree.get_path(NodeIndex::make(2, i0)).unwrap();
|
|
|
|
let i1 = 1;
|
|
let p1 = mtree.get_path(NodeIndex::make(2, i1)).unwrap();
|
|
|
|
let i2 = 2;
|
|
let p2 = mtree.get_path(NodeIndex::make(2, i2)).unwrap();
|
|
|
|
let i3 = 3;
|
|
let p3 = mtree.get_path(NodeIndex::make(2, i3)).unwrap();
|
|
|
|
let paths = [
|
|
(i0, VALUES4[i0 as usize], p0),
|
|
(i1, VALUES4[i1 as usize], p1),
|
|
(i2, VALUES4[i2 as usize], p2),
|
|
(i3, VALUES4[i3 as usize], p3),
|
|
];
|
|
|
|
let mut store = MerkleStore::default();
|
|
store.add_merkle_paths(paths.clone()).expect("the valid paths must work");
|
|
|
|
let pmt = PartialMerkleTree::with_paths(paths).unwrap();
|
|
|
|
// STORE LEAVES ARE CORRECT ==============================================================
|
|
// checks the leaves in the store corresponds to the expected values
|
|
assert_eq!(
|
|
store.get_node(pmt.root(), NodeIndex::make(pmt.max_depth(), 0)),
|
|
Ok(VALUES4[0]),
|
|
"node 0 must be in the pmt"
|
|
);
|
|
assert_eq!(
|
|
store.get_node(pmt.root(), NodeIndex::make(pmt.max_depth(), 1)),
|
|
Ok(VALUES4[1]),
|
|
"node 1 must be in the pmt"
|
|
);
|
|
assert_eq!(
|
|
store.get_node(pmt.root(), NodeIndex::make(pmt.max_depth(), 2)),
|
|
Ok(VALUES4[2]),
|
|
"node 2 must be in the pmt"
|
|
);
|
|
assert_eq!(
|
|
store.get_node(pmt.root(), NodeIndex::make(pmt.max_depth(), 3)),
|
|
Ok(VALUES4[3]),
|
|
"node 3 must be in the pmt"
|
|
);
|
|
|
|
// STORE LEAVES MATCH PMT ================================================================
|
|
// sanity check the values returned by the store and the pmt
|
|
assert_eq!(
|
|
pmt.get_node(NodeIndex::make(pmt.max_depth(), 0)),
|
|
store.get_node(pmt.root(), NodeIndex::make(pmt.max_depth(), 0)),
|
|
"node 0 must be the same for both PartialMerkleTree and MerkleStore"
|
|
);
|
|
assert_eq!(
|
|
pmt.get_node(NodeIndex::make(pmt.max_depth(), 1)),
|
|
store.get_node(pmt.root(), NodeIndex::make(pmt.max_depth(), 1)),
|
|
"node 1 must be the same for both PartialMerkleTree and MerkleStore"
|
|
);
|
|
assert_eq!(
|
|
pmt.get_node(NodeIndex::make(pmt.max_depth(), 2)),
|
|
store.get_node(pmt.root(), NodeIndex::make(pmt.max_depth(), 2)),
|
|
"node 2 must be the same for both PartialMerkleTree and MerkleStore"
|
|
);
|
|
assert_eq!(
|
|
pmt.get_node(NodeIndex::make(pmt.max_depth(), 3)),
|
|
store.get_node(pmt.root(), NodeIndex::make(pmt.max_depth(), 3)),
|
|
"node 3 must be the same for both PartialMerkleTree and MerkleStore"
|
|
);
|
|
|
|
// STORE MERKLE PATH MATCHS ==============================================================
|
|
// assert the merkle path returned by the store is the same as the one in the pmt
|
|
let result = store.get_path(pmt.root(), NodeIndex::make(pmt.max_depth(), 0)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[0], result.value,
|
|
"Value for merkle path at index 0 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
pmt.get_path(NodeIndex::make(pmt.max_depth(), 0)),
|
|
Ok(result.path),
|
|
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
let result = store.get_path(pmt.root(), NodeIndex::make(pmt.max_depth(), 1)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[1], result.value,
|
|
"Value for merkle path at index 0 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
pmt.get_path(NodeIndex::make(pmt.max_depth(), 1)),
|
|
Ok(result.path),
|
|
"merkle path for index 1 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
let result = store.get_path(pmt.root(), NodeIndex::make(pmt.max_depth(), 2)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[2], result.value,
|
|
"Value for merkle path at index 0 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
pmt.get_path(NodeIndex::make(pmt.max_depth(), 2)),
|
|
Ok(result.path),
|
|
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
let result = store.get_path(pmt.root(), NodeIndex::make(pmt.max_depth(), 3)).unwrap();
|
|
assert_eq!(
|
|
VALUES4[3], result.value,
|
|
"Value for merkle path at index 0 must match leaf value"
|
|
);
|
|
assert_eq!(
|
|
pmt.get_path(NodeIndex::make(pmt.max_depth(), 3)),
|
|
Ok(result.path),
|
|
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn wont_open_to_different_depth_root() {
|
|
let empty = EmptySubtreeRoots::empty_hashes(64);
|
|
let a = [ONE; 4];
|
|
let b = [Felt::new(2); 4];
|
|
|
|
// Compute the root for a different depth. We cherry-pick this specific depth to prevent a
|
|
// regression to a bug in the past that allowed the user to fetch a node at a depth lower than
|
|
// the inserted path of a Merkle tree.
|
|
let mut root = Rpo256::merge(&[a.into(), b.into()]);
|
|
for depth in (1..=63).rev() {
|
|
root = Rpo256::merge(&[root, empty[depth]]);
|
|
}
|
|
|
|
// For this example, the depth of the Merkle tree is 1, as we have only two leaves. Here we
|
|
// attempt to fetch a node on the maximum depth, and it should fail because the root shouldn't
|
|
// exist for the set.
|
|
let mtree = MerkleTree::new(vec![a, b]).unwrap();
|
|
let store = MerkleStore::from(&mtree);
|
|
let index = NodeIndex::root();
|
|
let err = store.get_node(root, index).err().unwrap();
|
|
assert_eq!(err, MerkleError::RootNotInStore(root));
|
|
}
|
|
|
|
#[test]
|
|
fn store_path_opens_from_leaf() {
|
|
let a = [ONE; 4];
|
|
let b = [Felt::new(2); 4];
|
|
let c = [Felt::new(3); 4];
|
|
let d = [Felt::new(4); 4];
|
|
let e = [Felt::new(5); 4];
|
|
let f = [Felt::new(6); 4];
|
|
let g = [Felt::new(7); 4];
|
|
let h = [Felt::new(8); 4];
|
|
|
|
let i = Rpo256::merge(&[a.into(), b.into()]);
|
|
let j = Rpo256::merge(&[c.into(), d.into()]);
|
|
let k = Rpo256::merge(&[e.into(), f.into()]);
|
|
let l = Rpo256::merge(&[g.into(), h.into()]);
|
|
|
|
let m = Rpo256::merge(&[i, j]);
|
|
let n = Rpo256::merge(&[k, l]);
|
|
|
|
let root = Rpo256::merge(&[m, n]);
|
|
|
|
let mtree = MerkleTree::new(vec![a, b, c, d, e, f, g, h]).unwrap();
|
|
let store = MerkleStore::from(&mtree);
|
|
let path = store.get_path(root, NodeIndex::make(3, 1)).unwrap().path;
|
|
|
|
let expected = MerklePath::new([a.into(), j, n].to_vec());
|
|
assert_eq!(path, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn test_set_node() -> Result<(), MerkleError> {
|
|
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
|
|
let mut store = MerkleStore::from(&mtree);
|
|
let value = int_to_node(42);
|
|
let index = NodeIndex::make(mtree.depth(), 0);
|
|
let new_root = store.set_node(mtree.root(), index, value)?.root;
|
|
assert_eq!(store.get_node(new_root, index), Ok(value), "Value must have changed");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_constructors() -> Result<(), MerkleError> {
|
|
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
|
|
let store = MerkleStore::from(&mtree);
|
|
|
|
let depth = mtree.depth();
|
|
let leaves = 2u64.pow(depth.into());
|
|
for index in 0..leaves {
|
|
let index = NodeIndex::make(depth, index);
|
|
let value_path = store.get_path(mtree.root(), index)?;
|
|
assert_eq!(mtree.get_path(index)?, value_path.path);
|
|
}
|
|
|
|
let depth = 32;
|
|
let smt =
|
|
SimpleSmt::with_leaves(depth, KEYS4.into_iter().zip(digests_to_words(&VALUES4))).unwrap();
|
|
let store = MerkleStore::from(&smt);
|
|
let depth = smt.depth();
|
|
|
|
for key in KEYS4 {
|
|
let index = NodeIndex::make(depth, key);
|
|
let value_path = store.get_path(smt.root(), index)?;
|
|
assert_eq!(smt.get_path(index)?, value_path.path);
|
|
}
|
|
|
|
let d = 2;
|
|
let paths = [
|
|
(0, VALUES4[0], mtree.get_path(NodeIndex::make(d, 0)).unwrap()),
|
|
(1, VALUES4[1], mtree.get_path(NodeIndex::make(d, 1)).unwrap()),
|
|
(2, VALUES4[2], mtree.get_path(NodeIndex::make(d, 2)).unwrap()),
|
|
(3, VALUES4[3], mtree.get_path(NodeIndex::make(d, 3)).unwrap()),
|
|
];
|
|
|
|
let mut store1 = MerkleStore::default();
|
|
store1.add_merkle_paths(paths.clone())?;
|
|
|
|
let mut store2 = MerkleStore::default();
|
|
store2.add_merkle_path(0, VALUES4[0], mtree.get_path(NodeIndex::make(d, 0))?)?;
|
|
store2.add_merkle_path(1, VALUES4[1], mtree.get_path(NodeIndex::make(d, 1))?)?;
|
|
store2.add_merkle_path(2, VALUES4[2], mtree.get_path(NodeIndex::make(d, 2))?)?;
|
|
store2.add_merkle_path(3, VALUES4[3], mtree.get_path(NodeIndex::make(d, 3))?)?;
|
|
let pmt = PartialMerkleTree::with_paths(paths).unwrap();
|
|
|
|
for key in [0, 1, 2, 3] {
|
|
let index = NodeIndex::make(d, key);
|
|
let value_path1 = store1.get_path(pmt.root(), index)?;
|
|
let value_path2 = store2.get_path(pmt.root(), index)?;
|
|
assert_eq!(value_path1, value_path2);
|
|
|
|
let index = NodeIndex::make(d, key);
|
|
assert_eq!(pmt.get_path(index)?, value_path1.path);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn node_path_should_be_truncated_by_midtier_insert() {
|
|
let key = 0b11010010_11001100_11001100_11001100_11001100_11001100_11001100_11001100_u64;
|
|
|
|
let mut store = MerkleStore::new();
|
|
let root: RpoDigest = EmptySubtreeRoots::empty_hashes(64)[0];
|
|
|
|
// insert first node - works as expected
|
|
let depth = 64;
|
|
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
|
|
let index = NodeIndex::new(depth, key).unwrap();
|
|
let root = store.set_node(root, index, node).unwrap().root;
|
|
let result = store.get_node(root, index).unwrap();
|
|
let path = store.get_path(root, index).unwrap().path;
|
|
assert_eq!(node, result);
|
|
assert_eq!(path.depth(), depth);
|
|
assert!(path.verify(index.value(), result, &root));
|
|
|
|
// flip the first bit of the key and insert the second node on a different depth
|
|
let key = key ^ (1 << 63);
|
|
let key = key >> 8;
|
|
let depth = 56;
|
|
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
|
|
let index = NodeIndex::new(depth, key).unwrap();
|
|
let root = store.set_node(root, index, node).unwrap().root;
|
|
let result = store.get_node(root, index).unwrap();
|
|
let path = store.get_path(root, index).unwrap().path;
|
|
assert_eq!(node, result);
|
|
assert_eq!(path.depth(), depth);
|
|
assert!(path.verify(index.value(), result, &root));
|
|
|
|
// attempt to fetch a path of the second node to depth 64
|
|
// should fail because the previously inserted node will remove its sub-tree from the set
|
|
let key = key << 8;
|
|
let index = NodeIndex::new(64, key).unwrap();
|
|
assert!(store.get_node(root, index).is_err());
|
|
}
|
|
|
|
// LEAF TRAVERSAL
|
|
// ================================================================================================
|
|
|
|
#[test]
|
|
fn get_leaf_depth_works_depth_64() {
|
|
let mut store = MerkleStore::new();
|
|
let mut root: RpoDigest = EmptySubtreeRoots::empty_hashes(64)[0];
|
|
let key = u64::MAX;
|
|
|
|
// this will create a rainbow tree and test all opening to depth 64
|
|
for d in 0..64 {
|
|
let k = key & (u64::MAX >> d);
|
|
let node = RpoDigest::from([Felt::new(k); WORD_SIZE]);
|
|
let index = NodeIndex::new(64, k).unwrap();
|
|
|
|
// assert the leaf doesn't exist before the insert. the returned depth should always
|
|
// increment with the paths count of the set, as they are insersecting one another up to
|
|
// the first bits of the used key.
|
|
assert_eq!(d, store.get_leaf_depth(root, 64, k).unwrap());
|
|
|
|
// insert and assert the correct depth
|
|
root = store.set_node(root, index, node).unwrap().root;
|
|
assert_eq!(64, store.get_leaf_depth(root, 64, k).unwrap());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn get_leaf_depth_works_with_incremental_depth() {
|
|
let mut store = MerkleStore::new();
|
|
let mut root: RpoDigest = EmptySubtreeRoots::empty_hashes(64)[0];
|
|
|
|
// insert some path to the left of the root and assert it
|
|
let key = 0b01001011_10110110_00001101_01110100_00111011_10101101_00000100_01000001_u64;
|
|
assert_eq!(0, store.get_leaf_depth(root, 64, key).unwrap());
|
|
let depth = 64;
|
|
let index = NodeIndex::new(depth, key).unwrap();
|
|
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
|
|
root = store.set_node(root, index, node).unwrap().root;
|
|
assert_eq!(depth, store.get_leaf_depth(root, 64, key).unwrap());
|
|
|
|
// flip the key to the right of the root and insert some content on depth 16
|
|
let key = 0b11001011_10110110_00000000_00000000_00000000_00000000_00000000_00000000_u64;
|
|
assert_eq!(1, store.get_leaf_depth(root, 64, key).unwrap());
|
|
let depth = 16;
|
|
let index = NodeIndex::new(depth, key >> (64 - depth)).unwrap();
|
|
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
|
|
root = store.set_node(root, index, node).unwrap().root;
|
|
assert_eq!(depth, store.get_leaf_depth(root, 64, key).unwrap());
|
|
|
|
// attempt the sibling of the previous leaf
|
|
let key = 0b11001011_10110111_00000000_00000000_00000000_00000000_00000000_00000000_u64;
|
|
assert_eq!(16, store.get_leaf_depth(root, 64, key).unwrap());
|
|
let index = NodeIndex::new(depth, key >> (64 - depth)).unwrap();
|
|
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
|
|
root = store.set_node(root, index, node).unwrap().root;
|
|
assert_eq!(depth, store.get_leaf_depth(root, 64, key).unwrap());
|
|
|
|
// move down to the next depth and assert correct behavior
|
|
let key = 0b11001011_10110100_00000000_00000000_00000000_00000000_00000000_00000000_u64;
|
|
assert_eq!(15, store.get_leaf_depth(root, 64, key).unwrap());
|
|
let depth = 17;
|
|
let index = NodeIndex::new(depth, key >> (64 - depth)).unwrap();
|
|
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
|
|
root = store.set_node(root, index, node).unwrap().root;
|
|
assert_eq!(depth, store.get_leaf_depth(root, 64, key).unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn get_leaf_depth_works_with_depth_8() {
|
|
let mut store = MerkleStore::new();
|
|
let mut root: RpoDigest = EmptySubtreeRoots::empty_hashes(8)[0];
|
|
|
|
// insert some random, 8 depth keys. `a` diverges from the first bit
|
|
let a = 0b01101001_u64;
|
|
let b = 0b10011001_u64;
|
|
let c = 0b10010110_u64;
|
|
let d = 0b11110110_u64;
|
|
|
|
for k in [a, b, c, d] {
|
|
let index = NodeIndex::new(8, k).unwrap();
|
|
let node = RpoDigest::from([Felt::new(k); WORD_SIZE]);
|
|
root = store.set_node(root, index, node).unwrap().root;
|
|
}
|
|
|
|
// assert all leaves returns the inserted depth
|
|
for k in [a, b, c, d] {
|
|
assert_eq!(8, store.get_leaf_depth(root, 8, k).unwrap());
|
|
}
|
|
|
|
// flip last bit of a and expect it to return the the same depth, but for an empty node
|
|
assert_eq!(8, store.get_leaf_depth(root, 8, 0b01101000_u64).unwrap());
|
|
|
|
// flip fourth bit of a and expect an empty node on depth 4
|
|
assert_eq!(4, store.get_leaf_depth(root, 8, 0b01111001_u64).unwrap());
|
|
|
|
// flip third bit of a and expect an empty node on depth 3
|
|
assert_eq!(3, store.get_leaf_depth(root, 8, 0b01001001_u64).unwrap());
|
|
|
|
// flip second bit of a and expect an empty node on depth 2
|
|
assert_eq!(2, store.get_leaf_depth(root, 8, 0b00101001_u64).unwrap());
|
|
|
|
// flip fourth bit of c and expect an empty node on depth 4
|
|
assert_eq!(4, store.get_leaf_depth(root, 8, 0b10000110_u64).unwrap());
|
|
|
|
// flip second bit of d and expect an empty node on depth 3 as depth 2 conflicts with b and c
|
|
assert_eq!(3, store.get_leaf_depth(root, 8, 0b10110110_u64).unwrap());
|
|
|
|
// duplicate the tree on `a` and assert the depth is short-circuited by such sub-tree
|
|
let index = NodeIndex::new(8, a).unwrap();
|
|
root = store.set_node(root, index, root).unwrap().root;
|
|
assert_eq!(Err(MerkleError::DepthTooBig(9)), store.get_leaf_depth(root, 8, a));
|
|
}
|
|
|
|
#[test]
|
|
fn find_lone_leaf() {
|
|
let mut store = MerkleStore::new();
|
|
let empty = EmptySubtreeRoots::empty_hashes(64);
|
|
let mut root: RpoDigest = empty[0];
|
|
|
|
// insert a single leaf into the store at depth 64
|
|
let key_a = 0b01010101_10101010_00001111_01110100_00111011_10101101_00000100_01000001_u64;
|
|
let idx_a = NodeIndex::make(64, key_a);
|
|
let val_a = RpoDigest::from([ONE, ONE, ONE, ONE]);
|
|
root = store.set_node(root, idx_a, val_a).unwrap().root;
|
|
|
|
// for every ancestor of A, A should be a long leaf
|
|
for depth in 1..64 {
|
|
let parent_index = NodeIndex::make(depth, key_a >> (64 - depth));
|
|
let parent = store.get_node(root, parent_index).unwrap();
|
|
|
|
let res = store.find_lone_leaf(parent, parent_index, 64).unwrap();
|
|
assert_eq!(res, Some((idx_a, val_a)));
|
|
}
|
|
|
|
// insert another leaf into the store such that it has the same 8 bit prefix as A
|
|
let key_b = 0b01010101_01111010_00001111_01110100_00111011_10101101_00000100_01000001_u64;
|
|
let idx_b = NodeIndex::make(64, key_b);
|
|
let val_b = RpoDigest::from([ONE, ONE, ONE, ZERO]);
|
|
root = store.set_node(root, idx_b, val_b).unwrap().root;
|
|
|
|
// for any node which is common between A and B, find_lone_leaf() should return None as the
|
|
// node has two descendants
|
|
for depth in 1..9 {
|
|
let parent_index = NodeIndex::make(depth, key_a >> (64 - depth));
|
|
let parent = store.get_node(root, parent_index).unwrap();
|
|
|
|
let res = store.find_lone_leaf(parent, parent_index, 64).unwrap();
|
|
assert_eq!(res, None);
|
|
}
|
|
|
|
// for other ancestors of A and B, A and B should be lone leaves respectively
|
|
for depth in 9..64 {
|
|
let parent_index = NodeIndex::make(depth, key_a >> (64 - depth));
|
|
let parent = store.get_node(root, parent_index).unwrap();
|
|
|
|
let res = store.find_lone_leaf(parent, parent_index, 64).unwrap();
|
|
assert_eq!(res, Some((idx_a, val_a)));
|
|
}
|
|
|
|
for depth in 9..64 {
|
|
let parent_index = NodeIndex::make(depth, key_b >> (64 - depth));
|
|
let parent = store.get_node(root, parent_index).unwrap();
|
|
|
|
let res = store.find_lone_leaf(parent, parent_index, 64).unwrap();
|
|
assert_eq!(res, Some((idx_b, val_b)));
|
|
}
|
|
|
|
// for any other node, find_lone_leaf() should return None as they have no leaf nodes
|
|
let parent_index = NodeIndex::make(16, 0b01010101_11111111);
|
|
let parent = store.get_node(root, parent_index).unwrap();
|
|
let res = store.find_lone_leaf(parent, parent_index, 64).unwrap();
|
|
assert_eq!(res, None);
|
|
}
|
|
|
|
// SUBSET EXTRACTION
|
|
// ================================================================================================
|
|
|
|
#[test]
|
|
fn mstore_subset() {
|
|
// add a Merkle tree of depth 3 to the store
|
|
let mtree = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
|
|
let mut store = MerkleStore::default();
|
|
let empty_store_num_nodes = store.nodes.len();
|
|
store.extend(mtree.inner_nodes());
|
|
|
|
// build 3 subtrees contained within the above Merkle tree; note that subtree2 is a subset
|
|
// of subtree1
|
|
let subtree1 = MerkleTree::new(digests_to_words(&VALUES8[..4])).unwrap();
|
|
let subtree2 = MerkleTree::new(digests_to_words(&VALUES8[2..4])).unwrap();
|
|
let subtree3 = MerkleTree::new(digests_to_words(&VALUES8[6..])).unwrap();
|
|
|
|
// --- extract all 3 subtrees ---------------------------------------------
|
|
|
|
let substore = store.subset([subtree1.root(), subtree2.root(), subtree3.root()].iter());
|
|
|
|
// number of nodes should increase by 4: 3 nodes form subtree1 and 1 node from subtree3
|
|
assert_eq!(substore.nodes.len(), empty_store_num_nodes + 4);
|
|
|
|
// make sure paths that all subtrees are in the store
|
|
check_mstore_subtree(&substore, &subtree1);
|
|
check_mstore_subtree(&substore, &subtree2);
|
|
check_mstore_subtree(&substore, &subtree3);
|
|
|
|
// --- extract subtrees 1 and 3 -------------------------------------------
|
|
// this should give the same result as above as subtree2 is nested within subtree1
|
|
|
|
let substore = store.subset([subtree1.root(), subtree3.root()].iter());
|
|
|
|
// number of nodes should increase by 4: 3 nodes form subtree1 and 1 node from subtree3
|
|
assert_eq!(substore.nodes.len(), empty_store_num_nodes + 4);
|
|
|
|
// make sure paths that all subtrees are in the store
|
|
check_mstore_subtree(&substore, &subtree1);
|
|
check_mstore_subtree(&substore, &subtree2);
|
|
check_mstore_subtree(&substore, &subtree3);
|
|
}
|
|
|
|
fn check_mstore_subtree(store: &MerkleStore, subtree: &MerkleTree) {
|
|
for (i, value) in subtree.leaves() {
|
|
let index = NodeIndex::new(subtree.depth(), i).unwrap();
|
|
let path1 = store.get_path(subtree.root(), index).unwrap();
|
|
assert_eq!(*path1.value, *value);
|
|
|
|
let path2 = subtree.get_path(index).unwrap();
|
|
assert_eq!(path1.path, path2);
|
|
}
|
|
}
|
|
|
|
// SERIALIZATION
|
|
// ================================================================================================
|
|
|
|
#[cfg(feature = "std")]
|
|
#[test]
|
|
fn test_serialization() -> Result<(), Box<dyn Error>> {
|
|
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
|
|
let store = MerkleStore::from(&mtree);
|
|
let decoded = MerkleStore::read_from_bytes(&store.to_bytes()).expect("deserialization failed");
|
|
assert_eq!(store, decoded);
|
|
Ok(())
|
|
}
|
|
|
|
// MERKLE RECORDER
|
|
// ================================================================================================
|
|
#[test]
|
|
fn test_recorder() {
|
|
// instantiate recorder from MerkleTree and SimpleSmt
|
|
let mtree = MerkleTree::new(digests_to_words(&VALUES4)).unwrap();
|
|
let smtree = SimpleSmt::with_leaves(
|
|
64,
|
|
KEYS8.into_iter().zip(VALUES8.into_iter().map(|x| x.into()).rev()),
|
|
)
|
|
.unwrap();
|
|
|
|
let mut recorder: RecordingMerkleStore =
|
|
mtree.inner_nodes().chain(smtree.inner_nodes()).collect();
|
|
|
|
// get nodes from both trees and make sure they are correct
|
|
let index_0 = NodeIndex::new(mtree.depth(), 0).unwrap();
|
|
let node = recorder.get_node(mtree.root(), index_0).unwrap();
|
|
assert_eq!(node, mtree.get_node(index_0).unwrap());
|
|
|
|
let index_1 = NodeIndex::new(smtree.depth(), 1).unwrap();
|
|
let node = recorder.get_node(smtree.root(), index_1).unwrap();
|
|
assert_eq!(node, smtree.get_node(index_1).unwrap());
|
|
|
|
// insert a value and assert that when we request it next time it is accurate
|
|
let new_value = [ZERO, ZERO, ONE, ONE].into();
|
|
let index_2 = NodeIndex::new(smtree.depth(), 2).unwrap();
|
|
let root = recorder.set_node(smtree.root(), index_2, new_value).unwrap().root;
|
|
assert_eq!(recorder.get_node(root, index_2).unwrap(), new_value);
|
|
|
|
// construct the proof
|
|
let rec_map = recorder.into_inner();
|
|
let (_, proof) = rec_map.finalize();
|
|
let merkle_store: MerkleStore = proof.into();
|
|
|
|
// make sure the proof contains all nodes from both trees
|
|
let node = merkle_store.get_node(mtree.root(), index_0).unwrap();
|
|
assert_eq!(node, mtree.get_node(index_0).unwrap());
|
|
|
|
let node = merkle_store.get_node(smtree.root(), index_1).unwrap();
|
|
assert_eq!(node, smtree.get_node(index_1).unwrap());
|
|
|
|
let node = merkle_store.get_node(smtree.root(), index_2).unwrap();
|
|
assert_eq!(node, smtree.get_leaf(index_2.value()).unwrap().into());
|
|
|
|
// assert that is doesnt contain nodes that were not recorded
|
|
let not_recorded_index = NodeIndex::new(smtree.depth(), 4).unwrap();
|
|
assert!(merkle_store.get_node(smtree.root(), not_recorded_index).is_err());
|
|
assert!(smtree.get_node(not_recorded_index).is_ok());
|
|
}
|