Merge pull request #154 from 0xPolygonMiden/next
Tracking PR for v0.6.0 release
This commit is contained in:
commit
18302d68e0
25 changed files with 1648 additions and 470 deletions
|
@ -1,3 +1,9 @@
|
|||
## 0.6.0 (2023-06-25)
|
||||
|
||||
* [BREAKING] Added support for recording capabilities for `MerkleStore` (#162).
|
||||
* [BREAKING] Refactored Merkle struct APIs to use `RpoDigest` instead of `Word` (#157).
|
||||
* Added initial implementation of `PartialMerkleTree` (#156).
|
||||
|
||||
## 0.5.0 (2023-05-26)
|
||||
|
||||
* Implemented `TieredSmt` (#152, #153).
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
[package]
|
||||
name = "miden-crypto"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
description = "Miden Cryptographic primitives"
|
||||
authors = ["miden contributors"]
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/0xPolygonMiden/crypto"
|
||||
documentation = "https://docs.rs/miden-crypto/0.5.0"
|
||||
documentation = "https://docs.rs/miden-crypto/0.6.0"
|
||||
categories = ["cryptography", "no-std"]
|
||||
keywords = ["miden", "crypto", "hash", "merkle"]
|
||||
edition = "2021"
|
||||
|
|
|
@ -12,10 +12,11 @@ For performance benchmarks of these hash functions and their comparison to other
|
|||
## Merkle
|
||||
[Merkle module](./src/merkle/) provides a set of data structures related to Merkle trees. All these data structures are implemented using the RPO hash function described above. The data structures are:
|
||||
|
||||
* `Mmr`: a Merkle mountain range structure designed to function as an append-only log.
|
||||
* `MerkleTree`: a regular fully-balanced binary Merkle tree. The depth of this tree can be at most 64.
|
||||
* `MerklePathSet`: a collection of Merkle authentication paths all resolving to the same root. The length of the paths can be at most 64.
|
||||
* `MerkleStore`: a collection of Merkle trees of different heights designed to efficiently store trees with common subtrees.
|
||||
* `Mmr`: a Merkle mountain range structure designed to function as an append-only log.
|
||||
* `MerkleStore`: a collection of Merkle trees of different heights designed to efficiently store trees with common subtrees. When instantiated with `RecordingMap`, a Merkle store records all accesses to the original data.
|
||||
* `PartialMerkleTree`: a partial view of a Merkle tree where some sub-trees may not be known. This is similar to a collection of Merkle paths all resolving to the same root. The length of the paths can be at most 64.
|
||||
* `SimpleSmt`: a Sparse Merkle Tree (with no compaction), mapping 64-bit keys to 4-element values.
|
||||
* `TieredSmt`: a Sparse Merkle tree (with compaction), mapping 4-element keys to 4-element values.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
|
||||
use miden_crypto::merkle::{MerkleStore, MerkleTree, NodeIndex, SimpleSmt};
|
||||
use miden_crypto::merkle::{DefaultMerkleStore as MerkleStore, MerkleTree, NodeIndex, SimpleSmt};
|
||||
use miden_crypto::Word;
|
||||
use miden_crypto::{hash::rpo::RpoDigest, Felt};
|
||||
use rand_utils::{rand_array, rand_value};
|
||||
|
@ -409,7 +409,7 @@ fn update_leaf_merkletree(c: &mut Criterion) {
|
|||
// The MerkleTree automatically updates its internal root, the Store maintains
|
||||
// the old root and adds the new one. Here we update the root to have a fair
|
||||
// comparison
|
||||
store_root = store.set_node(root, index, value).unwrap().root;
|
||||
store_root = store.set_node(root, index, value.into()).unwrap().root;
|
||||
black_box(store_root)
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
|
@ -455,7 +455,7 @@ fn update_leaf_simplesmt(c: &mut Criterion) {
|
|||
// The MerkleTree automatically updates its internal root, the Store maintains
|
||||
// the old root and adds the new one. Here we update the root to have a fair
|
||||
// comparison
|
||||
store_root = store.set_node(root, index, value).unwrap().root;
|
||||
store_root = store.set_node(root, index, value.into()).unwrap().root;
|
||||
black_box(store_root)
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
|
|
|
@ -2,7 +2,10 @@ use super::{
|
|||
Felt, FieldElement, Hasher, Rpo256, RpoDigest, StarkField, ALPHA, INV_ALPHA, ONE, STATE_WIDTH,
|
||||
ZERO,
|
||||
};
|
||||
use crate::utils::collections::{BTreeSet, Vec};
|
||||
use crate::{
|
||||
utils::collections::{BTreeSet, Vec},
|
||||
Word,
|
||||
};
|
||||
use core::convert::TryInto;
|
||||
use proptest::prelude::*;
|
||||
use rand_utils::rand_value;
|
||||
|
@ -203,7 +206,7 @@ fn sponge_bytes_with_remainder_length_wont_panic() {
|
|||
// size.
|
||||
//
|
||||
// this is a preliminary test to the fuzzy-stress of proptest.
|
||||
Rpo256::hash(&vec![0; 113]);
|
||||
Rpo256::hash(&[0; 113]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -227,12 +230,12 @@ fn sponge_zeroes_collision() {
|
|||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn rpo256_wont_panic_with_arbitrary_input(ref vec in any::<Vec<u8>>()) {
|
||||
Rpo256::hash(&vec);
|
||||
fn rpo256_wont_panic_with_arbitrary_input(ref bytes in any::<Vec<u8>>()) {
|
||||
Rpo256::hash(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
const EXPECTED: [[Felt; 4]; 19] = [
|
||||
const EXPECTED: [Word; 19] = [
|
||||
[
|
||||
Felt::new(1502364727743950833),
|
||||
Felt::new(5880949717274681448),
|
||||
|
|
|
@ -190,7 +190,7 @@ mod tests {
|
|||
if value > (1 << depth) { // round up
|
||||
depth += 1;
|
||||
}
|
||||
NodeIndex::new(depth, value.into()).unwrap()
|
||||
NodeIndex::new(depth, value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
use super::{
|
||||
Felt, InnerNodeInfo, MerkleError, MerklePath, NodeIndex, Rpo256, RpoDigest, Vec, Word,
|
||||
};
|
||||
use crate::{
|
||||
utils::{string::String, uninit_vector, word_to_hex},
|
||||
FieldElement,
|
||||
};
|
||||
use core::{fmt, slice};
|
||||
use super::{InnerNodeInfo, MerkleError, MerklePath, NodeIndex, Rpo256, RpoDigest, Vec, Word};
|
||||
use crate::utils::{string::String, uninit_vector, word_to_hex};
|
||||
use core::{fmt, ops::Deref, slice};
|
||||
use winter_math::log2;
|
||||
|
||||
// MERKLE TREE
|
||||
|
@ -14,7 +9,7 @@ use winter_math::log2;
|
|||
/// A fully-balanced binary Merkle tree (i.e., a tree where the number of leaves is a power of two).
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MerkleTree {
|
||||
nodes: Vec<Word>,
|
||||
nodes: Vec<RpoDigest>,
|
||||
}
|
||||
|
||||
impl MerkleTree {
|
||||
|
@ -34,10 +29,12 @@ impl MerkleTree {
|
|||
|
||||
// create un-initialized vector to hold all tree nodes
|
||||
let mut nodes = unsafe { uninit_vector(2 * n) };
|
||||
nodes[0] = [Felt::ZERO; 4];
|
||||
nodes[0] = RpoDigest::default();
|
||||
|
||||
// copy leaves into the second part of the nodes vector
|
||||
nodes[n..].copy_from_slice(&leaves);
|
||||
nodes[n..].iter_mut().zip(leaves).for_each(|(node, leaf)| {
|
||||
*node = RpoDigest::from(leaf);
|
||||
});
|
||||
|
||||
// re-interpret nodes as an array of two nodes fused together
|
||||
// Safety: `nodes` will never move here as it is not bound to an external lifetime (i.e.
|
||||
|
@ -47,7 +44,7 @@ impl MerkleTree {
|
|||
|
||||
// calculate all internal tree nodes
|
||||
for i in (1..n).rev() {
|
||||
nodes[i] = Rpo256::merge(&pairs[i]).into();
|
||||
nodes[i] = Rpo256::merge(&pairs[i]);
|
||||
}
|
||||
|
||||
Ok(Self { nodes })
|
||||
|
@ -57,7 +54,7 @@ impl MerkleTree {
|
|||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Returns the root of this Merkle tree.
|
||||
pub fn root(&self) -> Word {
|
||||
pub fn root(&self) -> RpoDigest {
|
||||
self.nodes[1]
|
||||
}
|
||||
|
||||
|
@ -74,7 +71,7 @@ impl MerkleTree {
|
|||
/// Returns an error if:
|
||||
/// * The specified depth is greater than the depth of the tree.
|
||||
/// * The specified index is not valid for the specified depth.
|
||||
pub fn get_node(&self, index: NodeIndex) -> Result<Word, MerkleError> {
|
||||
pub fn get_node(&self, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
|
||||
if index.is_root() {
|
||||
return Err(MerkleError::DepthTooSmall(index.depth()));
|
||||
} else if index.depth() > self.depth() {
|
||||
|
@ -120,7 +117,11 @@ impl MerkleTree {
|
|||
/// Returns an iterator over the leaves of this [MerkleTree].
|
||||
pub fn leaves(&self) -> impl Iterator<Item = (u64, &Word)> {
|
||||
let leaves_start = self.nodes.len() / 2;
|
||||
self.nodes.iter().skip(leaves_start).enumerate().map(|(i, v)| (i as u64, v))
|
||||
self.nodes
|
||||
.iter()
|
||||
.skip(leaves_start)
|
||||
.enumerate()
|
||||
.map(|(i, v)| (i as u64, v.deref()))
|
||||
}
|
||||
|
||||
/// Returns n iterator over every inner node of this [MerkleTree].
|
||||
|
@ -159,13 +160,13 @@ impl MerkleTree {
|
|||
|
||||
// update the current node
|
||||
let pos = index.to_scalar_index() as usize;
|
||||
self.nodes[pos] = value;
|
||||
self.nodes[pos] = value.into();
|
||||
|
||||
// traverse to the root, updating each node with the merged values of its parents
|
||||
for _ in 0..index.depth() {
|
||||
index.move_up();
|
||||
let pos = index.to_scalar_index() as usize;
|
||||
let value = Rpo256::merge(&pairs[pos]).into();
|
||||
let value = Rpo256::merge(&pairs[pos]);
|
||||
self.nodes[pos] = value;
|
||||
}
|
||||
|
||||
|
@ -180,7 +181,7 @@ impl MerkleTree {
|
|||
///
|
||||
/// Use this to extract the data of the tree, there is no guarantee on the order of the elements.
|
||||
pub struct InnerNodeIterator<'a> {
|
||||
nodes: &'a Vec<Word>,
|
||||
nodes: &'a Vec<RpoDigest>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
|
@ -258,13 +259,17 @@ pub fn path_to_text(path: &MerklePath) -> Result<String, fmt::Error> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::merkle::{int_to_node, InnerNodeInfo};
|
||||
use crate::{
|
||||
merkle::{digests_to_words, int_to_leaf, int_to_node, InnerNodeInfo},
|
||||
Felt, Word, WORD_SIZE,
|
||||
};
|
||||
use core::mem::size_of;
|
||||
use proptest::prelude::*;
|
||||
|
||||
const LEAVES4: [Word; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
|
||||
const LEAVES4: [RpoDigest; WORD_SIZE] =
|
||||
[int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
|
||||
|
||||
const LEAVES8: [Word; 8] = [
|
||||
const LEAVES8: [RpoDigest; 8] = [
|
||||
int_to_node(1),
|
||||
int_to_node(2),
|
||||
int_to_node(3),
|
||||
|
@ -277,7 +282,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn build_merkle_tree() {
|
||||
let tree = super::MerkleTree::new(LEAVES4.to_vec()).unwrap();
|
||||
let tree = super::MerkleTree::new(digests_to_words(&LEAVES4)).unwrap();
|
||||
assert_eq!(8, tree.nodes.len());
|
||||
|
||||
// leaves were copied correctly
|
||||
|
@ -296,7 +301,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn get_leaf() {
|
||||
let tree = super::MerkleTree::new(LEAVES4.to_vec()).unwrap();
|
||||
let tree = super::MerkleTree::new(digests_to_words(&LEAVES4)).unwrap();
|
||||
|
||||
// check depth 2
|
||||
assert_eq!(LEAVES4[0], tree.get_node(NodeIndex::make(2, 0)).unwrap());
|
||||
|
@ -313,7 +318,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn get_path() {
|
||||
let tree = super::MerkleTree::new(LEAVES4.to_vec()).unwrap();
|
||||
let tree = super::MerkleTree::new(digests_to_words(&LEAVES4)).unwrap();
|
||||
|
||||
let (_, node2, node3) = compute_internal_nodes();
|
||||
|
||||
|
@ -330,12 +335,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn update_leaf() {
|
||||
let mut tree = super::MerkleTree::new(LEAVES8.to_vec()).unwrap();
|
||||
let mut tree = super::MerkleTree::new(digests_to_words(&LEAVES8)).unwrap();
|
||||
|
||||
// update one leaf
|
||||
let value = 3;
|
||||
let new_node = int_to_node(9);
|
||||
let mut expected_leaves = LEAVES8.to_vec();
|
||||
let new_node = int_to_leaf(9);
|
||||
let mut expected_leaves = digests_to_words(&LEAVES8);
|
||||
expected_leaves[value as usize] = new_node;
|
||||
let expected_tree = super::MerkleTree::new(expected_leaves.clone()).unwrap();
|
||||
|
||||
|
@ -344,7 +349,7 @@ mod tests {
|
|||
|
||||
// update another leaf
|
||||
let value = 6;
|
||||
let new_node = int_to_node(10);
|
||||
let new_node = int_to_leaf(10);
|
||||
expected_leaves[value as usize] = new_node;
|
||||
let expected_tree = super::MerkleTree::new(expected_leaves.clone()).unwrap();
|
||||
|
||||
|
@ -354,7 +359,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn nodes() -> Result<(), MerkleError> {
|
||||
let tree = super::MerkleTree::new(LEAVES4.to_vec()).unwrap();
|
||||
let tree = super::MerkleTree::new(digests_to_words(&LEAVES4)).unwrap();
|
||||
let root = tree.root();
|
||||
let l1n0 = tree.get_node(NodeIndex::make(1, 0))?;
|
||||
let l1n1 = tree.get_node(NodeIndex::make(1, 1))?;
|
||||
|
@ -403,8 +408,8 @@ mod tests {
|
|||
let digest = RpoDigest::from(word);
|
||||
|
||||
// assert the addresses are different
|
||||
let word_ptr = (&word).as_ptr() as *const u8;
|
||||
let digest_ptr = (&digest).as_ptr() as *const u8;
|
||||
let word_ptr = word.as_ptr() as *const u8;
|
||||
let digest_ptr = digest.as_ptr() as *const u8;
|
||||
assert_ne!(word_ptr, digest_ptr);
|
||||
|
||||
// compare the bytes representation
|
||||
|
@ -417,11 +422,13 @@ mod tests {
|
|||
// HELPER FUNCTIONS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
fn compute_internal_nodes() -> (Word, Word, Word) {
|
||||
let node2 = Rpo256::hash_elements(&[LEAVES4[0], LEAVES4[1]].concat());
|
||||
let node3 = Rpo256::hash_elements(&[LEAVES4[2], LEAVES4[3]].concat());
|
||||
fn compute_internal_nodes() -> (RpoDigest, RpoDigest, RpoDigest) {
|
||||
let node2 =
|
||||
Rpo256::hash_elements(&[Word::from(LEAVES4[0]), Word::from(LEAVES4[1])].concat());
|
||||
let node3 =
|
||||
Rpo256::hash_elements(&[Word::from(LEAVES4[2]), Word::from(LEAVES4[3])].concat());
|
||||
let root = Rpo256::merge(&[node2, node3]);
|
||||
|
||||
(root.into(), node2.into(), node3.into())
|
||||
(root, node2, node3)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use super::{super::Vec, super::ZERO, Felt, MmrProof, Rpo256, Word};
|
||||
use super::{
|
||||
super::{RpoDigest, Vec, ZERO},
|
||||
Felt, MmrProof, Rpo256, Word,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct MmrPeaks {
|
||||
|
@ -25,7 +28,7 @@ pub struct MmrPeaks {
|
|||
/// leaves, starting from the peak with most children, to the one with least.
|
||||
///
|
||||
/// Invariant: The length of `peaks` must be equal to the number of true bits in `num_leaves`.
|
||||
pub peaks: Vec<Word>,
|
||||
pub peaks: Vec<RpoDigest>,
|
||||
}
|
||||
|
||||
impl MmrPeaks {
|
||||
|
@ -38,7 +41,7 @@ impl MmrPeaks {
|
|||
Rpo256::hash_elements(&self.flatten_and_pad_peaks()).into()
|
||||
}
|
||||
|
||||
pub fn verify(&self, value: Word, opening: MmrProof) -> bool {
|
||||
pub fn verify(&self, value: RpoDigest, opening: MmrProof) -> bool {
|
||||
let root = &self.peaks[opening.peak_index()];
|
||||
opening.merkle_path.verify(opening.relative_pos() as u64, value, root)
|
||||
}
|
||||
|
@ -72,7 +75,15 @@ impl MmrPeaks {
|
|||
};
|
||||
|
||||
let mut elements = Vec::with_capacity(len);
|
||||
elements.extend_from_slice(&self.peaks.as_slice().concat());
|
||||
elements.extend_from_slice(
|
||||
&self
|
||||
.peaks
|
||||
.as_slice()
|
||||
.iter()
|
||||
.map(|digest| digest.into())
|
||||
.collect::<Vec<Word>>()
|
||||
.concat(),
|
||||
);
|
||||
elements.resize(len, ZERO);
|
||||
elements
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
//! depths, i.e. as part of adding adding a new element to the forest the trees with same depth are
|
||||
//! merged, creating a new tree with depth d+1, this process is continued until the property is
|
||||
//! restabilished.
|
||||
use super::bit::TrueBitPositionIterator;
|
||||
use super::{
|
||||
super::{InnerNodeInfo, MerklePath, Vec},
|
||||
MmrPeaks, MmrProof, Rpo256, Word,
|
||||
super::{InnerNodeInfo, MerklePath, RpoDigest, Vec},
|
||||
bit::TrueBitPositionIterator,
|
||||
MmrPeaks, MmrProof, Rpo256,
|
||||
};
|
||||
use core::fmt::{Display, Formatter};
|
||||
|
||||
|
@ -28,6 +28,7 @@ use std::error::Error;
|
|||
///
|
||||
/// Since this is a full representation of the MMR, elements are never removed and the MMR will
|
||||
/// grow roughly `O(2n)` in number of leaf elements.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Mmr {
|
||||
/// Refer to the `forest` method documentation for details of the semantics of this value.
|
||||
pub(super) forest: usize,
|
||||
|
@ -38,7 +39,7 @@ pub struct Mmr {
|
|||
/// the elements of every tree in the forest to be stored in the same sequential buffer. It
|
||||
/// also means new elements can be added to the forest, and merging of trees is very cheap with
|
||||
/// no need to copy elements.
|
||||
pub(super) nodes: Vec<Word>,
|
||||
pub(super) nodes: Vec<RpoDigest>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
|
@ -129,7 +130,7 @@ impl Mmr {
|
|||
/// Note: The leaf position is the 0-indexed number corresponding to the order the leaves were
|
||||
/// added, this corresponds to the MMR size _prior_ to adding the element. So the 1st element
|
||||
/// has position 0, the second position 1, and so on.
|
||||
pub fn get(&self, pos: usize) -> Result<Word, MmrError> {
|
||||
pub fn get(&self, pos: usize) -> Result<RpoDigest, MmrError> {
|
||||
// find the target tree responsible for the MMR position
|
||||
let tree_bit =
|
||||
leaf_to_corresponding_tree(pos, self.forest).ok_or(MmrError::InvalidPosition(pos))?;
|
||||
|
@ -153,7 +154,7 @@ impl Mmr {
|
|||
}
|
||||
|
||||
/// Adds a new element to the MMR.
|
||||
pub fn add(&mut self, el: Word) {
|
||||
pub fn add(&mut self, el: RpoDigest) {
|
||||
// Note: every node is also a tree of size 1, adding an element to the forest creates a new
|
||||
// rooted-tree of size 1. This may temporarily break the invariant that every tree in the
|
||||
// forest has different sizes, the loop below will eagerly merge trees of same size and
|
||||
|
@ -164,7 +165,7 @@ impl Mmr {
|
|||
let mut right = el;
|
||||
let mut left_tree = 1;
|
||||
while self.forest & left_tree != 0 {
|
||||
right = *Rpo256::merge(&[self.nodes[left_offset].into(), right.into()]);
|
||||
right = Rpo256::merge(&[self.nodes[left_offset], right]);
|
||||
self.nodes.push(right);
|
||||
|
||||
left_offset = left_offset.saturating_sub(nodes_in_forest(left_tree));
|
||||
|
@ -176,7 +177,7 @@ impl Mmr {
|
|||
|
||||
/// Returns an accumulator representing the current state of the MMR.
|
||||
pub fn accumulator(&self) -> MmrPeaks {
|
||||
let peaks: Vec<Word> = TrueBitPositionIterator::new(self.forest)
|
||||
let peaks: Vec<RpoDigest> = TrueBitPositionIterator::new(self.forest)
|
||||
.rev()
|
||||
.map(|bit| nodes_in_forest(1 << bit))
|
||||
.scan(0, |offset, el| {
|
||||
|
@ -212,7 +213,7 @@ impl Mmr {
|
|||
relative_pos: usize,
|
||||
index_offset: usize,
|
||||
mut index: usize,
|
||||
) -> (Word, Vec<Word>) {
|
||||
) -> (RpoDigest, Vec<RpoDigest>) {
|
||||
// collect the Merkle path
|
||||
let mut tree_depth = tree_bit as usize;
|
||||
let mut path = Vec::with_capacity(tree_depth + 1);
|
||||
|
@ -247,7 +248,7 @@ impl Mmr {
|
|||
|
||||
impl<T> From<T> for Mmr
|
||||
where
|
||||
T: IntoIterator<Item = Word>,
|
||||
T: IntoIterator<Item = RpoDigest>,
|
||||
{
|
||||
fn from(values: T) -> Self {
|
||||
let mut mmr = Mmr::new();
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
use super::bit::TrueBitPositionIterator;
|
||||
use super::full::{high_bitmask, leaf_to_corresponding_tree, nodes_in_forest};
|
||||
use super::{
|
||||
super::{InnerNodeInfo, Vec, WORD_SIZE, ZERO},
|
||||
Mmr, MmrPeaks, Rpo256, Word,
|
||||
super::{InnerNodeInfo, Vec},
|
||||
bit::TrueBitPositionIterator,
|
||||
full::{high_bitmask, leaf_to_corresponding_tree, nodes_in_forest},
|
||||
Mmr, MmrPeaks, Rpo256,
|
||||
};
|
||||
use crate::{
|
||||
hash::rpo::RpoDigest,
|
||||
merkle::{int_to_node, MerklePath},
|
||||
Felt, Word,
|
||||
};
|
||||
use crate::merkle::{int_to_node, MerklePath};
|
||||
|
||||
#[test]
|
||||
fn test_position_equal_or_higher_than_leafs_is_never_contained() {
|
||||
|
@ -99,7 +103,7 @@ fn test_nodes_in_forest_single_bit() {
|
|||
}
|
||||
}
|
||||
|
||||
const LEAVES: [Word; 7] = [
|
||||
const LEAVES: [RpoDigest; 7] = [
|
||||
int_to_node(0),
|
||||
int_to_node(1),
|
||||
int_to_node(2),
|
||||
|
@ -114,14 +118,14 @@ fn test_mmr_simple() {
|
|||
let mut postorder = Vec::new();
|
||||
postorder.push(LEAVES[0]);
|
||||
postorder.push(LEAVES[1]);
|
||||
postorder.push(*Rpo256::hash_elements(&[LEAVES[0], LEAVES[1]].concat()));
|
||||
postorder.push(Rpo256::merge(&[LEAVES[0], LEAVES[1]]));
|
||||
postorder.push(LEAVES[2]);
|
||||
postorder.push(LEAVES[3]);
|
||||
postorder.push(*Rpo256::hash_elements(&[LEAVES[2], LEAVES[3]].concat()));
|
||||
postorder.push(*Rpo256::hash_elements(&[postorder[2], postorder[5]].concat()));
|
||||
postorder.push(Rpo256::merge(&[LEAVES[2], LEAVES[3]]));
|
||||
postorder.push(Rpo256::merge(&[postorder[2], postorder[5]]));
|
||||
postorder.push(LEAVES[4]);
|
||||
postorder.push(LEAVES[5]);
|
||||
postorder.push(*Rpo256::hash_elements(&[LEAVES[4], LEAVES[5]].concat()));
|
||||
postorder.push(Rpo256::merge(&[LEAVES[4], LEAVES[5]]));
|
||||
postorder.push(LEAVES[6]);
|
||||
|
||||
let mut mmr = Mmr::new();
|
||||
|
@ -195,8 +199,8 @@ fn test_mmr_simple() {
|
|||
#[test]
|
||||
fn test_mmr_open() {
|
||||
let mmr: Mmr = LEAVES.into();
|
||||
let h01: Word = Rpo256::hash_elements(&LEAVES[0..2].concat()).into();
|
||||
let h23: Word = Rpo256::hash_elements(&LEAVES[2..4].concat()).into();
|
||||
let h01 = Rpo256::merge(&[LEAVES[0], LEAVES[1]]);
|
||||
let h23 = Rpo256::merge(&[LEAVES[2], LEAVES[3]]);
|
||||
|
||||
// node at pos 7 is the root
|
||||
assert!(mmr.open(7).is_err(), "Element 7 is not in the tree, result should be None");
|
||||
|
@ -214,7 +218,7 @@ fn test_mmr_open() {
|
|||
"MmrProof should be valid for the current accumulator."
|
||||
);
|
||||
|
||||
// nodes 4,5 are detph 1
|
||||
// nodes 4,5 are depth 1
|
||||
let root_to_path = MerklePath::new(vec![LEAVES[4]]);
|
||||
let opening = mmr
|
||||
.open(5)
|
||||
|
@ -361,10 +365,10 @@ fn test_mmr_inner_nodes() {
|
|||
let mmr: Mmr = LEAVES.into();
|
||||
let nodes: Vec<InnerNodeInfo> = mmr.inner_nodes().collect();
|
||||
|
||||
let h01 = *Rpo256::hash_elements(&[LEAVES[0], LEAVES[1]].concat());
|
||||
let h23 = *Rpo256::hash_elements(&[LEAVES[2], LEAVES[3]].concat());
|
||||
let h0123 = *Rpo256::hash_elements(&[h01, h23].concat());
|
||||
let h45 = *Rpo256::hash_elements(&[LEAVES[4], LEAVES[5]].concat());
|
||||
let h01 = Rpo256::merge(&[LEAVES[0], LEAVES[1]]);
|
||||
let h23 = Rpo256::merge(&[LEAVES[2], LEAVES[3]]);
|
||||
let h0123 = Rpo256::merge(&[h01, h23]);
|
||||
let h45 = Rpo256::merge(&[LEAVES[4], LEAVES[5]]);
|
||||
let postorder = vec![
|
||||
InnerNodeInfo {
|
||||
value: h01,
|
||||
|
@ -396,17 +400,20 @@ fn test_mmr_hash_peaks() {
|
|||
let mmr: Mmr = LEAVES.into();
|
||||
let peaks = mmr.accumulator();
|
||||
|
||||
let first_peak = *Rpo256::merge(&[
|
||||
Rpo256::hash_elements(&[LEAVES[0], LEAVES[1]].concat()),
|
||||
Rpo256::hash_elements(&[LEAVES[2], LEAVES[3]].concat()),
|
||||
let first_peak = Rpo256::merge(&[
|
||||
Rpo256::merge(&[LEAVES[0], LEAVES[1]]),
|
||||
Rpo256::merge(&[LEAVES[2], LEAVES[3]]),
|
||||
]);
|
||||
let second_peak = *Rpo256::hash_elements(&[LEAVES[4], LEAVES[5]].concat());
|
||||
let second_peak = Rpo256::merge(&[LEAVES[4], LEAVES[5]]);
|
||||
let third_peak = LEAVES[6];
|
||||
|
||||
// minimum length is 16
|
||||
let mut expected_peaks = [first_peak, second_peak, third_peak].to_vec();
|
||||
expected_peaks.resize(16, [ZERO; WORD_SIZE]);
|
||||
assert_eq!(peaks.hash_peaks(), *Rpo256::hash_elements(&expected_peaks.as_slice().concat()));
|
||||
expected_peaks.resize(16, RpoDigest::default());
|
||||
assert_eq!(
|
||||
peaks.hash_peaks(),
|
||||
*Rpo256::hash_elements(&digests_to_elements(&expected_peaks))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -422,29 +429,29 @@ fn test_mmr_peaks_hash_less_than_16() {
|
|||
|
||||
// minimum length is 16
|
||||
let mut expected_peaks = peaks.clone();
|
||||
expected_peaks.resize(16, [ZERO; WORD_SIZE]);
|
||||
expected_peaks.resize(16, RpoDigest::default());
|
||||
assert_eq!(
|
||||
accumulator.hash_peaks(),
|
||||
*Rpo256::hash_elements(&expected_peaks.as_slice().concat())
|
||||
*Rpo256::hash_elements(&digests_to_elements(&expected_peaks))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mmr_peaks_hash_odd() {
|
||||
let peaks: Vec<_> = (0..=17).map(|i| int_to_node(i)).collect();
|
||||
let peaks: Vec<_> = (0..=17).map(int_to_node).collect();
|
||||
|
||||
let accumulator = MmrPeaks {
|
||||
num_leaves: (1 << peaks.len()) - 1,
|
||||
peaks: peaks.clone(),
|
||||
};
|
||||
|
||||
// odd length bigger than 16 is padded to the next even nubmer
|
||||
let mut expected_peaks = peaks.clone();
|
||||
expected_peaks.resize(18, [ZERO; WORD_SIZE]);
|
||||
// odd length bigger than 16 is padded to the next even number
|
||||
let mut expected_peaks = peaks;
|
||||
expected_peaks.resize(18, RpoDigest::default());
|
||||
assert_eq!(
|
||||
accumulator.hash_peaks(),
|
||||
*Rpo256::hash_elements(&expected_peaks.as_slice().concat())
|
||||
*Rpo256::hash_elements(&digests_to_elements(&expected_peaks))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -476,3 +483,10 @@ mod property_tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HELPER FUNCTIONS
|
||||
// ================================================================================================
|
||||
|
||||
fn digests_to_elements(digests: &[RpoDigest]) -> Vec<Felt> {
|
||||
digests.iter().flat_map(Word::from).collect()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{
|
||||
hash::rpo::{Rpo256, RpoDigest},
|
||||
utils::collections::{vec, BTreeMap, BTreeSet, Vec},
|
||||
utils::collections::{vec, BTreeMap, BTreeSet, KvMap, RecordingMap, Vec},
|
||||
Felt, StarkField, Word, WORD_SIZE, ZERO,
|
||||
};
|
||||
use core::fmt;
|
||||
|
@ -10,7 +10,6 @@ use core::fmt;
|
|||
|
||||
mod empty_roots;
|
||||
pub use empty_roots::EmptySubtreeRoots;
|
||||
use empty_roots::EMPTY_WORD;
|
||||
|
||||
mod index;
|
||||
pub use index::NodeIndex;
|
||||
|
@ -34,17 +33,20 @@ mod mmr;
|
|||
pub use mmr::{Mmr, MmrPeaks, MmrProof};
|
||||
|
||||
mod store;
|
||||
pub use store::MerkleStore;
|
||||
pub use store::{DefaultMerkleStore, MerkleStore, RecordingMerkleStore, StoreNode};
|
||||
|
||||
mod node;
|
||||
pub use node::InnerNodeInfo;
|
||||
|
||||
mod partial_mt;
|
||||
pub use partial_mt::PartialMerkleTree;
|
||||
|
||||
// ERRORS
|
||||
// ================================================================================================
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum MerkleError {
|
||||
ConflictingRoots(Vec<Word>),
|
||||
ConflictingRoots(Vec<RpoDigest>),
|
||||
DepthTooSmall(u8),
|
||||
DepthTooBig(u64),
|
||||
DuplicateValuesForIndex(u64),
|
||||
|
@ -54,9 +56,9 @@ pub enum MerkleError {
|
|||
InvalidPath(MerklePath),
|
||||
InvalidNumEntries(usize, usize),
|
||||
NodeNotInSet(NodeIndex),
|
||||
NodeNotInStore(Word, NodeIndex),
|
||||
NodeNotInStore(RpoDigest, NodeIndex),
|
||||
NumLeavesNotPowerOfTwo(usize),
|
||||
RootNotInStore(Word),
|
||||
RootNotInStore(RpoDigest),
|
||||
}
|
||||
|
||||
impl fmt::Display for MerkleError {
|
||||
|
@ -95,6 +97,16 @@ impl std::error::Error for MerkleError {}
|
|||
// ================================================================================================
|
||||
|
||||
#[cfg(test)]
|
||||
const fn int_to_node(value: u64) -> Word {
|
||||
const fn int_to_node(value: u64) -> RpoDigest {
|
||||
RpoDigest::new([Felt::new(value), ZERO, ZERO, ZERO])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
const fn int_to_leaf(value: u64) -> Word {
|
||||
[Felt::new(value), ZERO, ZERO, ZERO]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn digests_to_words(digests: &[RpoDigest]) -> Vec<Word> {
|
||||
digests.iter().map(|d| d.into()).collect()
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use super::Word;
|
||||
use crate::hash::rpo::RpoDigest;
|
||||
|
||||
/// Representation of a node with two children used for iterating over containers.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InnerNodeInfo {
|
||||
pub value: Word,
|
||||
pub left: Word,
|
||||
pub right: Word,
|
||||
pub value: RpoDigest,
|
||||
pub left: RpoDigest,
|
||||
pub right: RpoDigest,
|
||||
}
|
||||
|
|
329
src/merkle/partial_mt/mod.rs
Normal file
329
src/merkle/partial_mt/mod.rs
Normal file
|
@ -0,0 +1,329 @@
|
|||
use super::{
|
||||
BTreeMap, BTreeSet, MerkleError, MerklePath, NodeIndex, Rpo256, RpoDigest, ValuePath, Vec, ZERO,
|
||||
};
|
||||
use crate::utils::{format, string::String, word_to_hex};
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// CONSTANTS
|
||||
// ================================================================================================
|
||||
|
||||
/// Index of the root node.
|
||||
const ROOT_INDEX: NodeIndex = NodeIndex::root();
|
||||
|
||||
/// An RpoDigest consisting of 4 ZERO elements.
|
||||
const EMPTY_DIGEST: RpoDigest = RpoDigest::new([ZERO; 4]);
|
||||
|
||||
// PARTIAL MERKLE TREE
|
||||
// ================================================================================================
|
||||
|
||||
/// A partial Merkle tree with NodeIndex keys and 4-element RpoDigest leaf values. Partial Merkle
|
||||
/// Tree allows to create Merkle Tree by providing Merkle paths of different lengths.
|
||||
///
|
||||
/// The root of the tree is recomputed on each new leaf update.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PartialMerkleTree {
|
||||
max_depth: u8,
|
||||
nodes: BTreeMap<NodeIndex, RpoDigest>,
|
||||
leaves: BTreeSet<NodeIndex>,
|
||||
}
|
||||
|
||||
impl Default for PartialMerkleTree {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialMerkleTree {
|
||||
// CONSTANTS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Minimum supported depth.
|
||||
pub const MIN_DEPTH: u8 = 1;
|
||||
|
||||
/// Maximum supported depth.
|
||||
pub const MAX_DEPTH: u8 = 64;
|
||||
|
||||
// CONSTRUCTORS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Returns a new empty [PartialMerkleTree].
|
||||
pub fn new() -> Self {
|
||||
PartialMerkleTree {
|
||||
max_depth: 0,
|
||||
nodes: BTreeMap::new(),
|
||||
leaves: BTreeSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Appends the provided paths iterator into the set.
|
||||
///
|
||||
/// Analogous to [Self::add_path].
|
||||
pub fn with_paths<I>(paths: I) -> Result<Self, MerkleError>
|
||||
where
|
||||
I: IntoIterator<Item = (u64, RpoDigest, MerklePath)>,
|
||||
{
|
||||
// create an empty tree
|
||||
let tree = PartialMerkleTree::new();
|
||||
|
||||
paths.into_iter().try_fold(tree, |mut tree, (index, value, path)| {
|
||||
tree.add_path(index, value, path)?;
|
||||
Ok(tree)
|
||||
})
|
||||
}
|
||||
|
||||
// PUBLIC ACCESSORS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Returns the root of this Merkle tree.
|
||||
pub fn root(&self) -> RpoDigest {
|
||||
self.nodes.get(&ROOT_INDEX).cloned().unwrap_or(EMPTY_DIGEST)
|
||||
}
|
||||
|
||||
/// Returns the depth of this Merkle tree.
|
||||
pub fn max_depth(&self) -> u8 {
|
||||
self.max_depth
|
||||
}
|
||||
|
||||
/// Returns a node at the specified NodeIndex.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if the specified NodeIndex is not contained in the nodes map.
|
||||
pub fn get_node(&self, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
|
||||
self.nodes.get(&index).ok_or(MerkleError::NodeNotInSet(index)).map(|hash| *hash)
|
||||
}
|
||||
|
||||
/// Returns true if provided index contains in the leaves set, false otherwise.
|
||||
pub fn is_leaf(&self, index: NodeIndex) -> bool {
|
||||
self.leaves.contains(&index)
|
||||
}
|
||||
|
||||
/// Returns a vector of paths from every leaf to the root.
|
||||
pub fn paths(&self) -> Vec<(NodeIndex, ValuePath)> {
|
||||
let mut paths = Vec::new();
|
||||
self.leaves.iter().for_each(|&leaf| {
|
||||
paths.push((
|
||||
leaf,
|
||||
ValuePath {
|
||||
value: self.get_node(leaf).expect("Failed to get leaf node"),
|
||||
path: self.get_path(leaf).expect("Failed to get path"),
|
||||
},
|
||||
));
|
||||
});
|
||||
paths
|
||||
}
|
||||
|
||||
/// Returns a Merkle path from the node at the specified index to the root.
|
||||
///
|
||||
/// The node itself is not included in the path.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if:
|
||||
/// - the specified index has depth set to 0 or the depth is greater than the depth of this
|
||||
/// Merkle tree.
|
||||
/// - the specified index is not contained in the nodes map.
|
||||
pub fn get_path(&self, mut index: NodeIndex) -> Result<MerklePath, MerkleError> {
|
||||
if index.is_root() {
|
||||
return Err(MerkleError::DepthTooSmall(index.depth()));
|
||||
} else if index.depth() > self.max_depth() {
|
||||
return Err(MerkleError::DepthTooBig(index.depth() as u64));
|
||||
}
|
||||
|
||||
if !self.nodes.contains_key(&index) {
|
||||
return Err(MerkleError::NodeNotInSet(index));
|
||||
}
|
||||
|
||||
let mut path = Vec::new();
|
||||
for _ in 0..index.depth() {
|
||||
let sibling_index = index.sibling();
|
||||
index.move_up();
|
||||
let sibling =
|
||||
self.nodes.get(&sibling_index).cloned().expect("Sibling node not in the map");
|
||||
path.push(sibling);
|
||||
}
|
||||
Ok(MerklePath::new(path))
|
||||
}
|
||||
|
||||
// ITERATORS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Returns an iterator over the leaves of this [PartialMerkleTree].
|
||||
pub fn leaves(&self) -> impl Iterator<Item = (NodeIndex, RpoDigest)> + '_ {
|
||||
self.leaves.iter().map(|&leaf| {
|
||||
(
|
||||
leaf,
|
||||
self.get_node(leaf)
|
||||
.unwrap_or_else(|_| panic!("Leaf with {leaf} is not in the nodes map")),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// STATE MUTATORS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Adds the nodes of the specified Merkle path to this [PartialMerkleTree]. The `index_value`
|
||||
/// and `value` parameters specify the leaf node at which the path starts.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if:
|
||||
/// - The depth of the specified node_index is greater than 64 or smaller than 1.
|
||||
/// - The specified path is not consistent with other paths in the set (i.e., resolves to a
|
||||
/// different root).
|
||||
pub fn add_path(
|
||||
&mut self,
|
||||
index_value: u64,
|
||||
value: RpoDigest,
|
||||
path: MerklePath,
|
||||
) -> Result<(), MerkleError> {
|
||||
let index_value = NodeIndex::new(path.len() as u8, index_value)?;
|
||||
|
||||
Self::check_depth(index_value.depth())?;
|
||||
self.update_depth(index_value.depth());
|
||||
|
||||
// add provided node and its sibling to the leaves set
|
||||
self.leaves.insert(index_value);
|
||||
let sibling_node_index = index_value.sibling();
|
||||
self.leaves.insert(sibling_node_index);
|
||||
|
||||
// add provided node and its sibling to the nodes map
|
||||
self.nodes.insert(index_value, value);
|
||||
self.nodes.insert(sibling_node_index, path[0]);
|
||||
|
||||
// traverse to the root, updating the nodes
|
||||
let mut index_value = index_value;
|
||||
let node = Rpo256::merge(&index_value.build_node(value, path[0]));
|
||||
let root = path.iter().skip(1).copied().fold(node, |node, hash| {
|
||||
index_value.move_up();
|
||||
// insert calculated node to the nodes map
|
||||
self.nodes.insert(index_value, node);
|
||||
|
||||
// if the calculated node was a leaf, remove it from leaves set.
|
||||
self.leaves.remove(&index_value);
|
||||
|
||||
let sibling_node = index_value.sibling();
|
||||
|
||||
// Insert node from Merkle path to the nodes map. This sibling node becomes a leaf only
|
||||
// if it is a new node (it wasn't in nodes map).
|
||||
// Node can be in 3 states: internal node, leaf of the tree and not a tree node at all.
|
||||
// - Internal node can only stay in this state -- addition of a new path can't make it
|
||||
// a leaf or remove it from the tree.
|
||||
// - Leaf node can stay in the same state (remain a leaf) or can become an internal
|
||||
// node. In the first case we don't need to do anything, and the second case is handled
|
||||
// by the call of `self.leaves.remove(&index_value);`
|
||||
// - New node can be a calculated node or a "sibling" node from a Merkle Path:
|
||||
// --- Calculated node, obviously, never can be a leaf.
|
||||
// --- Sibling node can be only a leaf, because otherwise it is not a new node.
|
||||
if self.nodes.insert(sibling_node, hash).is_none() {
|
||||
self.leaves.insert(sibling_node);
|
||||
}
|
||||
|
||||
Rpo256::merge(&index_value.build_node(node, hash))
|
||||
});
|
||||
|
||||
// if the path set is empty (the root is all ZEROs), set the root to the root of the added
|
||||
// path; otherwise, the root of the added path must be identical to the current root
|
||||
if self.root() == EMPTY_DIGEST {
|
||||
self.nodes.insert(ROOT_INDEX, root);
|
||||
} else if self.root() != root {
|
||||
return Err(MerkleError::ConflictingRoots([self.root(), root].to_vec()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates value of the leaf at the specified index returning the old leaf value.
|
||||
///
|
||||
/// This also recomputes all hashes between the leaf and the root, updating the root itself.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if:
|
||||
/// - The depth of the specified node_index is greater than 64 or smaller than 1.
|
||||
/// - The specified node index is not corresponding to the leaf.
|
||||
pub fn update_leaf(
|
||||
&mut self,
|
||||
node_index: NodeIndex,
|
||||
value: RpoDigest,
|
||||
) -> Result<RpoDigest, MerkleError> {
|
||||
// check correctness of the depth and update it
|
||||
Self::check_depth(node_index.depth())?;
|
||||
self.update_depth(node_index.depth());
|
||||
|
||||
// insert NodeIndex to the leaves Set
|
||||
self.leaves.insert(node_index);
|
||||
|
||||
// add node value to the nodes Map
|
||||
let old_value = self
|
||||
.nodes
|
||||
.insert(node_index, value)
|
||||
.ok_or(MerkleError::NodeNotInSet(node_index))?;
|
||||
|
||||
// if the old value and new value are the same, there is nothing to update
|
||||
if value == old_value {
|
||||
return Ok(old_value);
|
||||
}
|
||||
|
||||
let mut node_index = node_index;
|
||||
let mut value = value;
|
||||
for _ in 0..node_index.depth() {
|
||||
let sibling = self.nodes.get(&node_index.sibling()).expect("sibling should exist");
|
||||
value = Rpo256::merge(&node_index.build_node(value, *sibling));
|
||||
node_index.move_up();
|
||||
self.nodes.insert(node_index, value);
|
||||
}
|
||||
|
||||
Ok(old_value)
|
||||
}
|
||||
|
||||
// UTILITY FUNCTIONS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Utility to visualize a [PartialMerkleTree] in text.
|
||||
pub fn print(&self) -> Result<String, fmt::Error> {
|
||||
let indent = " ";
|
||||
let mut s = String::new();
|
||||
s.push_str("root: ");
|
||||
s.push_str(&word_to_hex(&self.root())?);
|
||||
s.push('\n');
|
||||
for d in 1..=self.max_depth() {
|
||||
let entries = 2u64.pow(d.into());
|
||||
for i in 0..entries {
|
||||
let index = NodeIndex::new(d, i).expect("The index must always be valid");
|
||||
let node = self.get_node(index);
|
||||
let node = match node {
|
||||
Err(_) => continue,
|
||||
Ok(node) => node,
|
||||
};
|
||||
|
||||
for _ in 0..d {
|
||||
s.push_str(indent);
|
||||
}
|
||||
s.push_str(&format!("({}, {}): ", index.depth(), index.value()));
|
||||
s.push_str(&word_to_hex(&node)?);
|
||||
s.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
// HELPER METHODS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Updates depth value with the maximum of current and provided depth.
|
||||
fn update_depth(&mut self, new_depth: u8) {
|
||||
self.max_depth = new_depth.max(self.max_depth);
|
||||
}
|
||||
|
||||
/// Returns an error if the depth is 0 or is greater than 64.
|
||||
fn check_depth(depth: u8) -> Result<(), MerkleError> {
|
||||
// validate the range of the depth.
|
||||
if depth < Self::MIN_DEPTH {
|
||||
return Err(MerkleError::DepthTooSmall(depth));
|
||||
} else if Self::MAX_DEPTH < depth {
|
||||
return Err(MerkleError::DepthTooBig(depth as u64));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
313
src/merkle/partial_mt/tests.rs
Normal file
313
src/merkle/partial_mt/tests.rs
Normal file
|
@ -0,0 +1,313 @@
|
|||
use super::{
|
||||
super::{
|
||||
digests_to_words, int_to_node, DefaultMerkleStore as MerkleStore, MerkleTree, NodeIndex,
|
||||
PartialMerkleTree,
|
||||
},
|
||||
RpoDigest, ValuePath, Vec,
|
||||
};
|
||||
|
||||
// TEST DATA
|
||||
// ================================================================================================
|
||||
|
||||
const NODE10: NodeIndex = NodeIndex::new_unchecked(1, 0);
|
||||
const NODE11: NodeIndex = NodeIndex::new_unchecked(1, 1);
|
||||
|
||||
const NODE20: NodeIndex = NodeIndex::new_unchecked(2, 0);
|
||||
const NODE22: NodeIndex = NodeIndex::new_unchecked(2, 2);
|
||||
const NODE23: NodeIndex = NodeIndex::new_unchecked(2, 3);
|
||||
|
||||
const NODE30: NodeIndex = NodeIndex::new_unchecked(3, 0);
|
||||
const NODE31: NodeIndex = NodeIndex::new_unchecked(3, 1);
|
||||
const NODE32: NodeIndex = NodeIndex::new_unchecked(3, 2);
|
||||
const NODE33: NodeIndex = NodeIndex::new_unchecked(3, 3);
|
||||
|
||||
const VALUES8: [RpoDigest; 8] = [
|
||||
int_to_node(30),
|
||||
int_to_node(31),
|
||||
int_to_node(32),
|
||||
int_to_node(33),
|
||||
int_to_node(34),
|
||||
int_to_node(35),
|
||||
int_to_node(36),
|
||||
int_to_node(37),
|
||||
];
|
||||
|
||||
// TESTS
|
||||
// ================================================================================================
|
||||
|
||||
// For the Partial Merkle Tree tests we will use parts of the Merkle Tree which full form is
|
||||
// illustrated below:
|
||||
//
|
||||
// __________ root __________
|
||||
// / \
|
||||
// ____ 10 ____ ____ 11 ____
|
||||
// / \ / \
|
||||
// 20 21 22 23
|
||||
// / \ / \ / \ / \
|
||||
// (30) (31) (32) (33) (34) (35) (36) (37)
|
||||
//
|
||||
// Where node number is a concatenation of its depth and index. For example, node with
|
||||
// NodeIndex(3, 5) will be labeled as `35`. Leaves of the tree are shown as nodes with parenthesis
|
||||
// (33).
|
||||
|
||||
/// Checks that root returned by `root()` function is equal to the expected one.
|
||||
#[test]
|
||||
fn get_root() {
|
||||
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
|
||||
let expected_root = mt.root();
|
||||
|
||||
let ms = MerkleStore::from(&mt);
|
||||
let path33 = ms.get_path(expected_root, NODE33).unwrap();
|
||||
|
||||
let pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
|
||||
|
||||
assert_eq!(pmt.root(), expected_root);
|
||||
}
|
||||
|
||||
/// This test checks correctness of the `add_path()` and `get_path()` functions. First it creates a
|
||||
/// PMT using `add_path()` by adding Merkle Paths from node 33 and node 22 to the empty PMT. Then
|
||||
/// it checks that paths returned by `get_path()` function are equal to the expected ones.
|
||||
#[test]
|
||||
fn add_and_get_paths() {
|
||||
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
|
||||
let expected_root = mt.root();
|
||||
|
||||
let ms = MerkleStore::from(&mt);
|
||||
|
||||
let expected_path33 = ms.get_path(expected_root, NODE33).unwrap();
|
||||
let expected_path22 = ms.get_path(expected_root, NODE22).unwrap();
|
||||
|
||||
let mut pmt = PartialMerkleTree::new();
|
||||
pmt.add_path(3, expected_path33.value, expected_path33.path.clone()).unwrap();
|
||||
pmt.add_path(2, expected_path22.value, expected_path22.path.clone()).unwrap();
|
||||
|
||||
let path33 = pmt.get_path(NODE33).unwrap();
|
||||
let path22 = pmt.get_path(NODE22).unwrap();
|
||||
let actual_root = pmt.root();
|
||||
|
||||
assert_eq!(expected_path33.path, path33);
|
||||
assert_eq!(expected_path22.path, path22);
|
||||
assert_eq!(expected_root, actual_root);
|
||||
}
|
||||
|
||||
/// Checks that function `get_node` used on nodes 10 and 32 returns expected values.
|
||||
#[test]
|
||||
fn get_node() {
|
||||
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
|
||||
let expected_root = mt.root();
|
||||
|
||||
let ms = MerkleStore::from(&mt);
|
||||
|
||||
let path33 = ms.get_path(expected_root, NODE33).unwrap();
|
||||
|
||||
let pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
|
||||
|
||||
assert_eq!(ms.get_node(expected_root, NODE32).unwrap(), pmt.get_node(NODE32).unwrap());
|
||||
assert_eq!(ms.get_node(expected_root, NODE10).unwrap(), pmt.get_node(NODE10).unwrap());
|
||||
}
|
||||
|
||||
/// Updates leaves of the PMT using `update_leaf()` function and checks that new root of the tree
|
||||
/// is equal to the expected one.
|
||||
#[test]
|
||||
fn update_leaf() {
|
||||
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
|
||||
let root = mt.root();
|
||||
|
||||
let mut ms = MerkleStore::from(&mt);
|
||||
let path33 = ms.get_path(root, NODE33).unwrap();
|
||||
|
||||
let mut pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
|
||||
|
||||
let new_value32 = int_to_node(132);
|
||||
let expected_root = ms.set_node(root, NODE32, new_value32).unwrap().root;
|
||||
|
||||
pmt.update_leaf(NODE32, new_value32).unwrap();
|
||||
let actual_root = pmt.root();
|
||||
|
||||
assert_eq!(expected_root, actual_root);
|
||||
|
||||
let new_value20 = int_to_node(120);
|
||||
let expected_root = ms.set_node(expected_root, NODE20, new_value20).unwrap().root;
|
||||
|
||||
pmt.update_leaf(NODE20, new_value20).unwrap();
|
||||
let actual_root = pmt.root();
|
||||
|
||||
assert_eq!(expected_root, actual_root);
|
||||
}
|
||||
|
||||
/// Checks that paths of the PMT returned by `paths()` function are equal to the expected ones.
|
||||
#[test]
|
||||
fn get_paths() {
|
||||
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
|
||||
let expected_root = mt.root();
|
||||
|
||||
let ms = MerkleStore::from(&mt);
|
||||
|
||||
let path33 = ms.get_path(expected_root, NODE33).unwrap();
|
||||
let path22 = ms.get_path(expected_root, NODE22).unwrap();
|
||||
|
||||
let mut pmt = PartialMerkleTree::new();
|
||||
pmt.add_path(3, path33.value, path33.path).unwrap();
|
||||
pmt.add_path(2, path22.value, path22.path).unwrap();
|
||||
// After PMT creation with path33 (33; 32, 20, 11) and path22 (22; 23, 10) we will have this
|
||||
// tree:
|
||||
//
|
||||
// ______root______
|
||||
// / \
|
||||
// ___10___ ___11___
|
||||
// / \ / \
|
||||
// (20) 21 (22) (23)
|
||||
// / \
|
||||
// (32) (33)
|
||||
//
|
||||
// Which have leaf nodes 20, 22, 23, 32 and 33. Hence overall we will have 5 paths -- one path
|
||||
// for each leaf.
|
||||
|
||||
let leaves = vec![NODE20, NODE22, NODE23, NODE32, NODE33];
|
||||
let expected_paths: Vec<(NodeIndex, ValuePath)> = leaves
|
||||
.iter()
|
||||
.map(|&leaf| {
|
||||
(
|
||||
leaf,
|
||||
ValuePath {
|
||||
value: mt.get_node(leaf).unwrap(),
|
||||
path: mt.get_path(leaf).unwrap(),
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let actual_paths = pmt.paths();
|
||||
|
||||
assert_eq!(expected_paths, actual_paths);
|
||||
}
|
||||
|
||||
// Checks correctness of leaves determination when using the `leaves()` function.
|
||||
#[test]
|
||||
fn leaves() {
|
||||
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
|
||||
let expected_root = mt.root();
|
||||
|
||||
let ms = MerkleStore::from(&mt);
|
||||
|
||||
let path33 = ms.get_path(expected_root, NODE33).unwrap();
|
||||
let path22 = ms.get_path(expected_root, NODE22).unwrap();
|
||||
|
||||
let mut pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
|
||||
// After PMT creation with path33 (33; 32, 20, 11) we will have this tree:
|
||||
//
|
||||
// ______root______
|
||||
// / \
|
||||
// ___10___ (11)
|
||||
// / \
|
||||
// (20) 21
|
||||
// / \
|
||||
// (32) (33)
|
||||
//
|
||||
// Which have leaf nodes 11, 20, 32 and 33.
|
||||
|
||||
let value11 = mt.get_node(NODE11).unwrap();
|
||||
let value20 = mt.get_node(NODE20).unwrap();
|
||||
let value32 = mt.get_node(NODE32).unwrap();
|
||||
let value33 = mt.get_node(NODE33).unwrap();
|
||||
|
||||
let leaves = vec![(NODE11, value11), (NODE20, value20), (NODE32, value32), (NODE33, value33)];
|
||||
|
||||
let expected_leaves = leaves.iter().copied();
|
||||
assert!(expected_leaves.eq(pmt.leaves()));
|
||||
|
||||
pmt.add_path(2, path22.value, path22.path).unwrap();
|
||||
// After adding the path22 (22; 23, 10) to the existing PMT we will have this tree:
|
||||
//
|
||||
// ______root______
|
||||
// / \
|
||||
// ___10___ ___11___
|
||||
// / \ / \
|
||||
// (20) 21 (22) (23)
|
||||
// / \
|
||||
// (32) (33)
|
||||
//
|
||||
// Which have leaf nodes 20, 22, 23, 32 and 33.
|
||||
|
||||
let value20 = mt.get_node(NODE20).unwrap();
|
||||
let value22 = mt.get_node(NODE22).unwrap();
|
||||
let value23 = mt.get_node(NODE23).unwrap();
|
||||
let value32 = mt.get_node(NODE32).unwrap();
|
||||
let value33 = mt.get_node(NODE33).unwrap();
|
||||
|
||||
let leaves = vec![
|
||||
(NODE20, value20),
|
||||
(NODE22, value22),
|
||||
(NODE23, value23),
|
||||
(NODE32, value32),
|
||||
(NODE33, value33),
|
||||
];
|
||||
|
||||
let expected_leaves = leaves.iter().copied();
|
||||
assert!(expected_leaves.eq(pmt.leaves()));
|
||||
}
|
||||
|
||||
/// Checks that addition of the path with different root will cause an error.
|
||||
#[test]
|
||||
fn err_add_path() {
|
||||
let path33 = vec![int_to_node(1), int_to_node(2), int_to_node(3)].into();
|
||||
let path22 = vec![int_to_node(4), int_to_node(5)].into();
|
||||
|
||||
let mut pmt = PartialMerkleTree::new();
|
||||
pmt.add_path(3, int_to_node(6), path33).unwrap();
|
||||
|
||||
assert!(pmt.add_path(2, int_to_node(7), path22).is_err());
|
||||
}
|
||||
|
||||
/// Checks that the request of the node which is not in the PMT will cause an error.
|
||||
#[test]
|
||||
fn err_get_node() {
|
||||
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
|
||||
let expected_root = mt.root();
|
||||
|
||||
let ms = MerkleStore::from(&mt);
|
||||
|
||||
let path33 = ms.get_path(expected_root, NODE33).unwrap();
|
||||
|
||||
let pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
|
||||
|
||||
assert!(pmt.get_node(NODE22).is_err());
|
||||
assert!(pmt.get_node(NODE23).is_err());
|
||||
assert!(pmt.get_node(NODE30).is_err());
|
||||
assert!(pmt.get_node(NODE31).is_err());
|
||||
}
|
||||
|
||||
/// Checks that the request of the path from the leaf which is not in the PMT will cause an error.
|
||||
#[test]
|
||||
fn err_get_path() {
|
||||
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
|
||||
let expected_root = mt.root();
|
||||
|
||||
let ms = MerkleStore::from(&mt);
|
||||
|
||||
let path33 = ms.get_path(expected_root, NODE33).unwrap();
|
||||
|
||||
let pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
|
||||
|
||||
assert!(pmt.get_path(NODE22).is_err());
|
||||
assert!(pmt.get_path(NODE23).is_err());
|
||||
assert!(pmt.get_path(NODE30).is_err());
|
||||
assert!(pmt.get_path(NODE31).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn err_update_leaf() {
|
||||
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
|
||||
let expected_root = mt.root();
|
||||
|
||||
let ms = MerkleStore::from(&mt);
|
||||
|
||||
let path33 = ms.get_path(expected_root, NODE33).unwrap();
|
||||
|
||||
let mut pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
|
||||
|
||||
assert!(pmt.update_leaf(NODE22, int_to_node(22)).is_err());
|
||||
assert!(pmt.update_leaf(NODE23, int_to_node(23)).is_err());
|
||||
assert!(pmt.update_leaf(NODE30, int_to_node(30)).is_err());
|
||||
assert!(pmt.update_leaf(NODE31, int_to_node(31)).is_err());
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use super::{vec, InnerNodeInfo, MerkleError, NodeIndex, Rpo256, Vec, Word};
|
||||
use super::{vec, InnerNodeInfo, MerkleError, NodeIndex, Rpo256, RpoDigest, Vec};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
// MERKLE PATH
|
||||
|
@ -7,7 +7,7 @@ use core::ops::{Deref, DerefMut};
|
|||
/// A merkle path container, composed of a sequence of nodes of a Merkle tree.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct MerklePath {
|
||||
nodes: Vec<Word>,
|
||||
nodes: Vec<RpoDigest>,
|
||||
}
|
||||
|
||||
impl MerklePath {
|
||||
|
@ -15,7 +15,7 @@ impl MerklePath {
|
|||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Creates a new Merkle path from a list of nodes.
|
||||
pub fn new(nodes: Vec<Word>) -> Self {
|
||||
pub fn new(nodes: Vec<RpoDigest>) -> Self {
|
||||
Self { nodes }
|
||||
}
|
||||
|
||||
|
@ -28,13 +28,13 @@ impl MerklePath {
|
|||
}
|
||||
|
||||
/// Computes the merkle root for this opening.
|
||||
pub fn compute_root(&self, index: u64, node: Word) -> Result<Word, MerkleError> {
|
||||
pub fn compute_root(&self, index: u64, node: RpoDigest) -> Result<RpoDigest, MerkleError> {
|
||||
let mut index = NodeIndex::new(self.depth(), index)?;
|
||||
let root = self.nodes.iter().copied().fold(node, |node, sibling| {
|
||||
// compute the node and move to the next iteration.
|
||||
let input = index.build_node(node.into(), sibling.into());
|
||||
let input = index.build_node(node, sibling);
|
||||
index.move_up();
|
||||
Rpo256::merge(&input).into()
|
||||
Rpo256::merge(&input)
|
||||
});
|
||||
Ok(root)
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ impl MerklePath {
|
|||
/// Verifies the Merkle opening proof towards the provided root.
|
||||
///
|
||||
/// Returns `true` if `node` exists at `index` in a Merkle tree with `root`.
|
||||
pub fn verify(&self, index: u64, node: Word, root: &Word) -> bool {
|
||||
pub fn verify(&self, index: u64, node: RpoDigest, root: &RpoDigest) -> bool {
|
||||
match self.compute_root(index, node) {
|
||||
Ok(computed_root) => root == &computed_root,
|
||||
Err(_) => false,
|
||||
|
@ -55,7 +55,11 @@ impl MerklePath {
|
|||
///
|
||||
/// # Errors
|
||||
/// Returns an error if the specified index is not valid for this path.
|
||||
pub fn inner_nodes(&self, index: u64, node: Word) -> Result<InnerNodeIterator, MerkleError> {
|
||||
pub fn inner_nodes(
|
||||
&self,
|
||||
index: u64,
|
||||
node: RpoDigest,
|
||||
) -> Result<InnerNodeIterator, MerkleError> {
|
||||
Ok(InnerNodeIterator {
|
||||
nodes: &self.nodes,
|
||||
index: NodeIndex::new(self.depth(), index)?,
|
||||
|
@ -64,8 +68,14 @@ impl MerklePath {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Word>> for MerklePath {
|
||||
fn from(path: Vec<Word>) -> Self {
|
||||
impl From<MerklePath> for Vec<RpoDigest> {
|
||||
fn from(path: MerklePath) -> Self {
|
||||
path.nodes
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<RpoDigest>> for MerklePath {
|
||||
fn from(path: Vec<RpoDigest>) -> Self {
|
||||
Self::new(path)
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +83,7 @@ impl From<Vec<Word>> for MerklePath {
|
|||
impl Deref for MerklePath {
|
||||
// we use `Vec` here instead of slice so we can call vector mutation methods directly from the
|
||||
// merkle path (example: `Vec::remove`).
|
||||
type Target = Vec<Word>;
|
||||
type Target = Vec<RpoDigest>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.nodes
|
||||
|
@ -89,15 +99,15 @@ impl DerefMut for MerklePath {
|
|||
// ITERATORS
|
||||
// ================================================================================================
|
||||
|
||||
impl FromIterator<Word> for MerklePath {
|
||||
fn from_iter<T: IntoIterator<Item = Word>>(iter: T) -> Self {
|
||||
impl FromIterator<RpoDigest> for MerklePath {
|
||||
fn from_iter<T: IntoIterator<Item = RpoDigest>>(iter: T) -> Self {
|
||||
Self::new(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for MerklePath {
|
||||
type Item = Word;
|
||||
type IntoIter = vec::IntoIter<Word>;
|
||||
type Item = RpoDigest;
|
||||
type IntoIter = vec::IntoIter<RpoDigest>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.nodes.into_iter()
|
||||
|
@ -106,9 +116,9 @@ impl IntoIterator for MerklePath {
|
|||
|
||||
/// An iterator over internal nodes of a [MerklePath].
|
||||
pub struct InnerNodeIterator<'a> {
|
||||
nodes: &'a Vec<Word>,
|
||||
nodes: &'a Vec<RpoDigest>,
|
||||
index: NodeIndex,
|
||||
value: Word,
|
||||
value: RpoDigest,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for InnerNodeIterator<'a> {
|
||||
|
@ -123,7 +133,7 @@ impl<'a> Iterator for InnerNodeIterator<'a> {
|
|||
(self.value, self.nodes[sibling_pos])
|
||||
};
|
||||
|
||||
self.value = Rpo256::merge(&[left.into(), right.into()]).into();
|
||||
self.value = Rpo256::merge(&[left, right]);
|
||||
self.index.move_up();
|
||||
|
||||
Some(InnerNodeInfo {
|
||||
|
@ -144,7 +154,7 @@ impl<'a> Iterator for InnerNodeIterator<'a> {
|
|||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ValuePath {
|
||||
/// The node value opening for `path`.
|
||||
pub value: Word,
|
||||
pub value: RpoDigest,
|
||||
/// The path from `value` to `root` (exclusive).
|
||||
pub path: MerklePath,
|
||||
}
|
||||
|
@ -156,7 +166,7 @@ pub struct ValuePath {
|
|||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct RootPath {
|
||||
/// The node value opening for `path`.
|
||||
pub root: Word,
|
||||
pub root: RpoDigest,
|
||||
/// The path from `value` to `root` (exclusive).
|
||||
pub path: MerklePath,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use super::{BTreeMap, MerkleError, MerklePath, NodeIndex, Rpo256, ValuePath, Vec, Word, ZERO};
|
||||
use super::{BTreeMap, MerkleError, MerklePath, NodeIndex, Rpo256, ValuePath, Vec};
|
||||
use crate::{hash::rpo::RpoDigest, Word};
|
||||
|
||||
// MERKLE PATH SET
|
||||
// ================================================================================================
|
||||
|
@ -6,7 +7,7 @@ use super::{BTreeMap, MerkleError, MerklePath, NodeIndex, Rpo256, ValuePath, Vec
|
|||
/// A set of Merkle paths.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MerklePathSet {
|
||||
root: Word,
|
||||
root: RpoDigest,
|
||||
total_depth: u8,
|
||||
paths: BTreeMap<u64, MerklePath>,
|
||||
}
|
||||
|
@ -17,7 +18,7 @@ impl MerklePathSet {
|
|||
|
||||
/// Returns an empty MerklePathSet.
|
||||
pub fn new(depth: u8) -> Self {
|
||||
let root = [ZERO; 4];
|
||||
let root = RpoDigest::default();
|
||||
let paths = BTreeMap::new();
|
||||
|
||||
Self {
|
||||
|
@ -32,10 +33,10 @@ impl MerklePathSet {
|
|||
/// Analogous to `[Self::add_path]`.
|
||||
pub fn with_paths<I>(self, paths: I) -> Result<Self, MerkleError>
|
||||
where
|
||||
I: IntoIterator<Item = (u64, Word, MerklePath)>,
|
||||
I: IntoIterator<Item = (u64, RpoDigest, MerklePath)>,
|
||||
{
|
||||
paths.into_iter().try_fold(self, |mut set, (index, value, path)| {
|
||||
set.add_path(index, value, path)?;
|
||||
set.add_path(index, value.into(), path)?;
|
||||
Ok(set)
|
||||
})
|
||||
}
|
||||
|
@ -44,7 +45,7 @@ impl MerklePathSet {
|
|||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Returns the root to which all paths in this set resolve.
|
||||
pub const fn root(&self) -> Word {
|
||||
pub const fn root(&self) -> RpoDigest {
|
||||
self.root
|
||||
}
|
||||
|
||||
|
@ -61,7 +62,7 @@ impl MerklePathSet {
|
|||
/// Returns an error if:
|
||||
/// * The specified index is not valid for the depth of structure.
|
||||
/// * Requested node does not exist in the set.
|
||||
pub fn get_node(&self, index: NodeIndex) -> Result<Word, MerkleError> {
|
||||
pub fn get_node(&self, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
|
||||
if index.depth() != self.total_depth {
|
||||
return Err(MerkleError::InvalidDepth {
|
||||
expected: self.total_depth,
|
||||
|
@ -84,7 +85,7 @@ impl MerklePathSet {
|
|||
/// * Leaf with the requested path does not exist in the set.
|
||||
pub fn get_leaf(&self, index: u64) -> Result<Word, MerkleError> {
|
||||
let index = NodeIndex::new(self.depth(), index)?;
|
||||
self.get_node(index)
|
||||
Ok(self.get_node(index)?.into())
|
||||
}
|
||||
|
||||
/// Returns a Merkle path to the node at the specified index. The node itself is
|
||||
|
@ -163,18 +164,18 @@ impl MerklePathSet {
|
|||
|
||||
// update the current path
|
||||
let parity = index_value & 1;
|
||||
path.insert(parity as usize, value);
|
||||
path.insert(parity as usize, value.into());
|
||||
|
||||
// traverse to the root, updating the nodes
|
||||
let root: Word = Rpo256::merge(&[path[0].into(), path[1].into()]).into();
|
||||
let root = Rpo256::merge(&[path[0], path[1]]);
|
||||
let root = path.iter().skip(2).copied().fold(root, |root, hash| {
|
||||
index.move_up();
|
||||
Rpo256::merge(&index.build_node(root.into(), hash.into())).into()
|
||||
Rpo256::merge(&index.build_node(root, hash))
|
||||
});
|
||||
|
||||
// if the path set is empty (the root is all ZEROs), set the root to the root of the added
|
||||
// path; otherwise, the root of the added path must be identical to the current root
|
||||
if self.root == [ZERO; 4] {
|
||||
if self.root == RpoDigest::default() {
|
||||
self.root = root;
|
||||
} else if self.root != root {
|
||||
return Err(MerkleError::ConflictingRoots([self.root, root].to_vec()));
|
||||
|
@ -203,24 +204,24 @@ impl MerklePathSet {
|
|||
// Fill old_hashes vector -----------------------------------------------------------------
|
||||
let mut current_index = index;
|
||||
let mut old_hashes = Vec::with_capacity(path.len().saturating_sub(2));
|
||||
let mut root: Word = Rpo256::merge(&[path[0].into(), path[1].into()]).into();
|
||||
let mut root = Rpo256::merge(&[path[0], path[1]]);
|
||||
for hash in path.iter().skip(2).copied() {
|
||||
old_hashes.push(root);
|
||||
current_index.move_up();
|
||||
let input = current_index.build_node(hash.into(), root.into());
|
||||
root = Rpo256::merge(&input).into();
|
||||
let input = current_index.build_node(hash, root);
|
||||
root = Rpo256::merge(&input);
|
||||
}
|
||||
|
||||
// Fill new_hashes vector -----------------------------------------------------------------
|
||||
path[index.is_value_odd() as usize] = value;
|
||||
path[index.is_value_odd() as usize] = value.into();
|
||||
|
||||
let mut new_hashes = Vec::with_capacity(path.len().saturating_sub(2));
|
||||
let mut new_root: Word = Rpo256::merge(&[path[0].into(), path[1].into()]).into();
|
||||
let mut new_root = Rpo256::merge(&[path[0], path[1]]);
|
||||
for path_hash in path.iter().skip(2).copied() {
|
||||
new_hashes.push(new_root);
|
||||
index.move_up();
|
||||
let input = current_index.build_node(path_hash.into(), new_root.into());
|
||||
new_root = Rpo256::merge(&input).into();
|
||||
let input = current_index.build_node(path_hash, new_root);
|
||||
new_root = Rpo256::merge(&input);
|
||||
}
|
||||
|
||||
self.root = new_root;
|
||||
|
@ -245,7 +246,7 @@ impl MerklePathSet {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::merkle::int_to_node;
|
||||
use crate::merkle::{int_to_leaf, int_to_node};
|
||||
|
||||
#[test]
|
||||
fn get_root() {
|
||||
|
@ -318,20 +319,20 @@ mod tests {
|
|||
])
|
||||
.unwrap();
|
||||
|
||||
let new_hash_6 = int_to_node(100);
|
||||
let new_hash_5 = int_to_node(55);
|
||||
let new_hash_6 = int_to_leaf(100);
|
||||
let new_hash_5 = int_to_leaf(55);
|
||||
|
||||
set.update_leaf(index_6, new_hash_6).unwrap();
|
||||
let new_path_4 = set.get_path(NodeIndex::make(depth, index_4)).unwrap();
|
||||
let new_hash_67 = calculate_parent_hash(new_hash_6, 14_u64, hash_7);
|
||||
let new_hash_67 = calculate_parent_hash(new_hash_6.into(), 14_u64, hash_7);
|
||||
assert_eq!(new_hash_67, new_path_4[1]);
|
||||
|
||||
set.update_leaf(index_5, new_hash_5).unwrap();
|
||||
let new_path_4 = set.get_path(NodeIndex::make(depth, index_4)).unwrap();
|
||||
let new_path_6 = set.get_path(NodeIndex::make(depth, index_6)).unwrap();
|
||||
let new_hash_45 = calculate_parent_hash(new_hash_5, 13_u64, hash_4);
|
||||
let new_hash_45 = calculate_parent_hash(new_hash_5.into(), 13_u64, hash_4);
|
||||
assert_eq!(new_hash_45, new_path_6[1]);
|
||||
assert_eq!(new_hash_5, new_path_4[0]);
|
||||
assert_eq!(RpoDigest::from(new_hash_5), new_path_4[0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -345,45 +346,45 @@ mod tests {
|
|||
let g = int_to_node(7);
|
||||
let h = int_to_node(8);
|
||||
|
||||
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 i = Rpo256::merge(&[a, b]);
|
||||
let j = Rpo256::merge(&[c, d]);
|
||||
let k = Rpo256::merge(&[e, f]);
|
||||
let l = Rpo256::merge(&[g, h]);
|
||||
|
||||
let m = Rpo256::merge(&[i.into(), j.into()]);
|
||||
let n = Rpo256::merge(&[k.into(), l.into()]);
|
||||
let m = Rpo256::merge(&[i, j]);
|
||||
let n = Rpo256::merge(&[k, l]);
|
||||
|
||||
let root = Rpo256::merge(&[m.into(), n.into()]);
|
||||
let root = Rpo256::merge(&[m, n]);
|
||||
|
||||
let mut set = MerklePathSet::new(3);
|
||||
|
||||
let value = b;
|
||||
let index = 1;
|
||||
let path = MerklePath::new([a.into(), j.into(), n.into()].to_vec());
|
||||
set.add_path(index, value, path.clone()).unwrap();
|
||||
assert_eq!(value, set.get_leaf(index).unwrap());
|
||||
assert_eq!(Word::from(root), set.root());
|
||||
let path = MerklePath::new([a, j, n].to_vec());
|
||||
set.add_path(index, value.into(), path).unwrap();
|
||||
assert_eq!(*value, set.get_leaf(index).unwrap());
|
||||
assert_eq!(root, set.root());
|
||||
|
||||
let value = e;
|
||||
let index = 4;
|
||||
let path = MerklePath::new([f.into(), l.into(), m.into()].to_vec());
|
||||
set.add_path(index, value, path.clone()).unwrap();
|
||||
assert_eq!(value, set.get_leaf(index).unwrap());
|
||||
assert_eq!(Word::from(root), set.root());
|
||||
let path = MerklePath::new([f, l, m].to_vec());
|
||||
set.add_path(index, value.into(), path).unwrap();
|
||||
assert_eq!(*value, set.get_leaf(index).unwrap());
|
||||
assert_eq!(root, set.root());
|
||||
|
||||
let value = a;
|
||||
let index = 0;
|
||||
let path = MerklePath::new([b.into(), j.into(), n.into()].to_vec());
|
||||
set.add_path(index, value, path.clone()).unwrap();
|
||||
assert_eq!(value, set.get_leaf(index).unwrap());
|
||||
assert_eq!(Word::from(root), set.root());
|
||||
let path = MerklePath::new([b, j, n].to_vec());
|
||||
set.add_path(index, value.into(), path).unwrap();
|
||||
assert_eq!(*value, set.get_leaf(index).unwrap());
|
||||
assert_eq!(root, set.root());
|
||||
|
||||
let value = h;
|
||||
let index = 7;
|
||||
let path = MerklePath::new([g.into(), k.into(), m.into()].to_vec());
|
||||
set.add_path(index, value, path.clone()).unwrap();
|
||||
assert_eq!(value, set.get_leaf(index).unwrap());
|
||||
assert_eq!(Word::from(root), set.root());
|
||||
let path = MerklePath::new([g, k, m].to_vec());
|
||||
set.add_path(index, value.into(), path).unwrap();
|
||||
assert_eq!(*value, set.get_leaf(index).unwrap());
|
||||
assert_eq!(root, set.root());
|
||||
}
|
||||
|
||||
// HELPER FUNCTIONS
|
||||
|
@ -397,11 +398,11 @@ mod tests {
|
|||
/// - node — current node
|
||||
/// - node_pos — position of the current node
|
||||
/// - sibling — neighboring vertex in the tree
|
||||
fn calculate_parent_hash(node: Word, node_pos: u64, sibling: Word) -> Word {
|
||||
fn calculate_parent_hash(node: RpoDigest, node_pos: u64, sibling: RpoDigest) -> RpoDigest {
|
||||
if is_even(node_pos) {
|
||||
Rpo256::merge(&[node.into(), sibling.into()]).into()
|
||||
Rpo256::merge(&[node, sibling])
|
||||
} else {
|
||||
Rpo256::merge(&[sibling.into(), node.into()]).into()
|
||||
Rpo256::merge(&[sibling, node])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{
|
||||
BTreeMap, BTreeSet, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, NodeIndex,
|
||||
Rpo256, RpoDigest, Vec, Word, EMPTY_WORD,
|
||||
Rpo256, RpoDigest, Vec, Word,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -15,7 +15,7 @@ mod tests;
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SimpleSmt {
|
||||
depth: u8,
|
||||
root: Word,
|
||||
root: RpoDigest,
|
||||
leaves: BTreeMap<u64, Word>,
|
||||
branches: BTreeMap<NodeIndex, BranchNode>,
|
||||
empty_hashes: Vec<RpoDigest>,
|
||||
|
@ -31,6 +31,9 @@ impl SimpleSmt {
|
|||
/// Maximum supported depth.
|
||||
pub const MAX_DEPTH: u8 = 64;
|
||||
|
||||
/// Value of an empty leaf.
|
||||
pub const EMPTY_VALUE: Word = super::empty_roots::EMPTY_WORD;
|
||||
|
||||
// CONSTRUCTORS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -49,7 +52,7 @@ impl SimpleSmt {
|
|||
}
|
||||
|
||||
let empty_hashes = EmptySubtreeRoots::empty_hashes(depth).to_vec();
|
||||
let root = empty_hashes[0].into();
|
||||
let root = empty_hashes[0];
|
||||
|
||||
Ok(Self {
|
||||
root,
|
||||
|
@ -91,12 +94,12 @@ impl SimpleSmt {
|
|||
let mut empty_entries = BTreeSet::new();
|
||||
for (key, value) in entries {
|
||||
let old_value = tree.update_leaf(key, value)?;
|
||||
if old_value != EMPTY_WORD || empty_entries.contains(&key) {
|
||||
if old_value != Self::EMPTY_VALUE || empty_entries.contains(&key) {
|
||||
return Err(MerkleError::DuplicateValuesForIndex(key));
|
||||
}
|
||||
// if we've processed an empty entry, add the key to the set of empty entry keys, and
|
||||
// if this key was already in the set, return an error
|
||||
if value == EMPTY_WORD && !empty_entries.insert(key) {
|
||||
if value == Self::EMPTY_VALUE && !empty_entries.insert(key) {
|
||||
return Err(MerkleError::DuplicateValuesForIndex(key));
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +110,7 @@ impl SimpleSmt {
|
|||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Returns the root of this Merkle tree.
|
||||
pub const fn root(&self) -> Word {
|
||||
pub const fn root(&self) -> RpoDigest {
|
||||
self.root
|
||||
}
|
||||
|
||||
|
@ -121,7 +124,7 @@ impl SimpleSmt {
|
|||
/// # Errors
|
||||
/// Returns an error if the specified index has depth set to 0 or the depth is greater than
|
||||
/// the depth of this Merkle tree.
|
||||
pub fn get_node(&self, index: NodeIndex) -> Result<Word, MerkleError> {
|
||||
pub fn get_node(&self, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
|
||||
if index.is_root() {
|
||||
Err(MerkleError::DepthTooSmall(index.depth()))
|
||||
} else if index.depth() > self.depth() {
|
||||
|
@ -129,11 +132,12 @@ impl SimpleSmt {
|
|||
} else if index.depth() == self.depth() {
|
||||
// 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.
|
||||
Ok(self
|
||||
.get_leaf_node(index.value())
|
||||
.unwrap_or_else(|| self.empty_hashes[index.depth() as usize].into()))
|
||||
Ok(RpoDigest::from(
|
||||
self.get_leaf_node(index.value())
|
||||
.unwrap_or_else(|| *self.empty_hashes[index.depth() as usize]),
|
||||
))
|
||||
} else {
|
||||
Ok(self.get_branch_node(&index).parent().into())
|
||||
Ok(self.get_branch_node(&index).parent())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,7 +147,7 @@ impl SimpleSmt {
|
|||
/// Returns an error if the index is greater than the maximum tree capacity, that is 2^{depth}.
|
||||
pub fn get_leaf(&self, index: u64) -> Result<Word, MerkleError> {
|
||||
let index = NodeIndex::new(self.depth, index)?;
|
||||
self.get_node(index)
|
||||
Ok(self.get_node(index)?.into())
|
||||
}
|
||||
|
||||
/// Returns a Merkle path from the node at the specified index to the root.
|
||||
|
@ -166,9 +170,9 @@ impl SimpleSmt {
|
|||
index.move_up();
|
||||
let BranchNode { left, right } = self.get_branch_node(&index);
|
||||
let value = if is_right { left } else { right };
|
||||
path.push(*value);
|
||||
path.push(value);
|
||||
}
|
||||
Ok(path.into())
|
||||
Ok(MerklePath::new(path))
|
||||
}
|
||||
|
||||
/// Return a Merkle path from the leaf at the specified index to the root.
|
||||
|
@ -193,9 +197,9 @@ impl SimpleSmt {
|
|||
/// Returns an iterator over the inner nodes of this Merkle tree.
|
||||
pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
|
||||
self.branches.values().map(|e| InnerNodeInfo {
|
||||
value: e.parent().into(),
|
||||
left: e.left.into(),
|
||||
right: e.right.into(),
|
||||
value: e.parent(),
|
||||
left: e.left,
|
||||
right: e.right,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -209,7 +213,7 @@ impl SimpleSmt {
|
|||
/// # Errors
|
||||
/// Returns an error if the index is greater than the maximum tree capacity, that is 2^{depth}.
|
||||
pub fn update_leaf(&mut self, index: u64, value: Word) -> Result<Word, MerkleError> {
|
||||
let old_value = self.insert_leaf_node(index, value).unwrap_or(EMPTY_WORD);
|
||||
let old_value = self.insert_leaf_node(index, value).unwrap_or(Self::EMPTY_VALUE);
|
||||
|
||||
// if the old value and new value are the same, there is nothing to update
|
||||
if value == old_value {
|
||||
|
@ -226,7 +230,7 @@ impl SimpleSmt {
|
|||
self.insert_branch_node(index, left, right);
|
||||
value = Rpo256::merge(&[left, right]);
|
||||
}
|
||||
self.root = value.into();
|
||||
self.root = value;
|
||||
Ok(old_value)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use super::{
|
||||
super::{int_to_node, InnerNodeInfo, MerkleError, MerkleTree, RpoDigest, SimpleSmt},
|
||||
NodeIndex, Rpo256, Vec, Word, EMPTY_WORD,
|
||||
super::{InnerNodeInfo, MerkleError, MerkleTree, RpoDigest, SimpleSmt},
|
||||
NodeIndex, Rpo256, Vec,
|
||||
};
|
||||
use crate::{
|
||||
merkle::{digests_to_words, empty_roots::EMPTY_WORD, int_to_leaf, int_to_node},
|
||||
Word,
|
||||
};
|
||||
|
||||
// TEST DATA
|
||||
|
@ -9,9 +13,9 @@ use super::{
|
|||
const KEYS4: [u64; 4] = [0, 1, 2, 3];
|
||||
const KEYS8: [u64; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
|
||||
|
||||
const VALUES4: [Word; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
|
||||
const VALUES4: [RpoDigest; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
|
||||
|
||||
const VALUES8: [Word; 8] = [
|
||||
const VALUES8: [RpoDigest; 8] = [
|
||||
int_to_node(1),
|
||||
int_to_node(2),
|
||||
int_to_node(3),
|
||||
|
@ -22,7 +26,7 @@ const VALUES8: [Word; 8] = [
|
|||
int_to_node(8),
|
||||
];
|
||||
|
||||
const ZERO_VALUES8: [Word; 8] = [int_to_node(0); 8];
|
||||
const ZERO_VALUES8: [Word; 8] = [int_to_leaf(0); 8];
|
||||
|
||||
// TESTS
|
||||
// ================================================================================================
|
||||
|
@ -42,7 +46,7 @@ fn build_sparse_tree() {
|
|||
|
||||
// insert single value
|
||||
let key = 6;
|
||||
let new_node = int_to_node(7);
|
||||
let new_node = int_to_leaf(7);
|
||||
values[key as usize] = new_node;
|
||||
let old_value = smt.update_leaf(key, new_node).expect("Failed to update leaf");
|
||||
let mt2 = MerkleTree::new(values.clone()).unwrap();
|
||||
|
@ -55,7 +59,7 @@ fn build_sparse_tree() {
|
|||
|
||||
// insert second value at distinct leaf branch
|
||||
let key = 2;
|
||||
let new_node = int_to_node(3);
|
||||
let new_node = int_to_leaf(3);
|
||||
values[key as usize] = new_node;
|
||||
let old_value = smt.update_leaf(key, new_node).expect("Failed to update leaf");
|
||||
let mt3 = MerkleTree::new(values).unwrap();
|
||||
|
@ -69,7 +73,9 @@ fn build_sparse_tree() {
|
|||
|
||||
#[test]
|
||||
fn test_depth2_tree() {
|
||||
let tree = SimpleSmt::with_leaves(2, KEYS4.into_iter().zip(VALUES4.into_iter())).unwrap();
|
||||
let tree =
|
||||
SimpleSmt::with_leaves(2, KEYS4.into_iter().zip(digests_to_words(&VALUES4).into_iter()))
|
||||
.unwrap();
|
||||
|
||||
// check internal structure
|
||||
let (root, node2, node3) = compute_internal_nodes();
|
||||
|
@ -96,7 +102,9 @@ fn test_depth2_tree() {
|
|||
|
||||
#[test]
|
||||
fn test_inner_node_iterator() -> Result<(), MerkleError> {
|
||||
let tree = SimpleSmt::with_leaves(2, KEYS4.into_iter().zip(VALUES4.into_iter())).unwrap();
|
||||
let tree =
|
||||
SimpleSmt::with_leaves(2, KEYS4.into_iter().zip(digests_to_words(&VALUES4).into_iter()))
|
||||
.unwrap();
|
||||
|
||||
// check depth 2
|
||||
assert_eq!(VALUES4[0], tree.get_node(NodeIndex::make(2, 0)).unwrap());
|
||||
|
@ -116,19 +124,19 @@ fn test_inner_node_iterator() -> Result<(), MerkleError> {
|
|||
let nodes: Vec<InnerNodeInfo> = tree.inner_nodes().collect();
|
||||
let expected = vec![
|
||||
InnerNodeInfo {
|
||||
value: root.into(),
|
||||
left: l1n0.into(),
|
||||
right: l1n1.into(),
|
||||
value: root,
|
||||
left: l1n0,
|
||||
right: l1n1,
|
||||
},
|
||||
InnerNodeInfo {
|
||||
value: l1n0.into(),
|
||||
left: l2n0.into(),
|
||||
right: l2n1.into(),
|
||||
value: l1n0,
|
||||
left: l2n0,
|
||||
right: l2n1,
|
||||
},
|
||||
InnerNodeInfo {
|
||||
value: l1n1.into(),
|
||||
left: l2n2.into(),
|
||||
right: l2n3.into(),
|
||||
value: l1n1,
|
||||
left: l2n2,
|
||||
right: l2n3,
|
||||
},
|
||||
];
|
||||
assert_eq!(nodes, expected);
|
||||
|
@ -138,28 +146,30 @@ fn test_inner_node_iterator() -> Result<(), MerkleError> {
|
|||
|
||||
#[test]
|
||||
fn update_leaf() {
|
||||
let mut tree = SimpleSmt::with_leaves(3, KEYS8.into_iter().zip(VALUES8.into_iter())).unwrap();
|
||||
let mut tree =
|
||||
SimpleSmt::with_leaves(3, KEYS8.into_iter().zip(digests_to_words(&VALUES8).into_iter()))
|
||||
.unwrap();
|
||||
|
||||
// update one value
|
||||
let key = 3;
|
||||
let new_node = int_to_node(9);
|
||||
let mut expected_values = VALUES8.to_vec();
|
||||
let new_node = int_to_leaf(9);
|
||||
let mut expected_values = digests_to_words(&VALUES8);
|
||||
expected_values[key] = new_node;
|
||||
let expected_tree = MerkleTree::new(expected_values.clone()).unwrap();
|
||||
|
||||
let old_leaf = tree.update_leaf(key as u64, new_node).unwrap();
|
||||
assert_eq!(expected_tree.root(), tree.root);
|
||||
assert_eq!(old_leaf, VALUES8[key]);
|
||||
assert_eq!(old_leaf, *VALUES8[key]);
|
||||
|
||||
// update another value
|
||||
let key = 6;
|
||||
let new_node = int_to_node(10);
|
||||
let new_node = int_to_leaf(10);
|
||||
expected_values[key] = new_node;
|
||||
let expected_tree = MerkleTree::new(expected_values.clone()).unwrap();
|
||||
|
||||
let old_leaf = tree.update_leaf(key as u64, new_node).unwrap();
|
||||
assert_eq!(expected_tree.root(), tree.root);
|
||||
assert_eq!(old_leaf, VALUES8[key]);
|
||||
assert_eq!(old_leaf, *VALUES8[key]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -172,34 +182,34 @@ fn small_tree_opening_is_consistent() {
|
|||
// / \ / \ / \ / \
|
||||
// a b 0 0 c 0 0 d
|
||||
|
||||
let z = Word::from(RpoDigest::default());
|
||||
let z = EMPTY_WORD;
|
||||
|
||||
let a = Word::from(Rpo256::merge(&[z.into(); 2]));
|
||||
let b = Word::from(Rpo256::merge(&[a.into(); 2]));
|
||||
let c = Word::from(Rpo256::merge(&[b.into(); 2]));
|
||||
let d = Word::from(Rpo256::merge(&[c.into(); 2]));
|
||||
|
||||
let e = Word::from(Rpo256::merge(&[a.into(), b.into()]));
|
||||
let f = Word::from(Rpo256::merge(&[z.into(), z.into()]));
|
||||
let g = Word::from(Rpo256::merge(&[c.into(), z.into()]));
|
||||
let h = Word::from(Rpo256::merge(&[z.into(), d.into()]));
|
||||
let e = Rpo256::merge(&[a.into(), b.into()]);
|
||||
let f = Rpo256::merge(&[z.into(), z.into()]);
|
||||
let g = Rpo256::merge(&[c.into(), z.into()]);
|
||||
let h = Rpo256::merge(&[z.into(), d.into()]);
|
||||
|
||||
let i = Word::from(Rpo256::merge(&[e.into(), f.into()]));
|
||||
let j = Word::from(Rpo256::merge(&[g.into(), h.into()]));
|
||||
let i = Rpo256::merge(&[e, f]);
|
||||
let j = Rpo256::merge(&[g, h]);
|
||||
|
||||
let k = Word::from(Rpo256::merge(&[i.into(), j.into()]));
|
||||
let k = Rpo256::merge(&[i, j]);
|
||||
|
||||
let depth = 3;
|
||||
let entries = vec![(0, a), (1, b), (4, c), (7, d)];
|
||||
let tree = SimpleSmt::with_leaves(depth, entries).unwrap();
|
||||
|
||||
assert_eq!(tree.root(), Word::from(k));
|
||||
assert_eq!(tree.root(), k);
|
||||
|
||||
let cases: Vec<(u8, u64, Vec<Word>)> = vec![
|
||||
(3, 0, vec![b, f, j]),
|
||||
(3, 1, vec![a, f, j]),
|
||||
(3, 4, vec![z, h, i]),
|
||||
(3, 7, vec![z, g, i]),
|
||||
let cases: Vec<(u8, u64, Vec<RpoDigest>)> = vec![
|
||||
(3, 0, vec![b.into(), f, j]),
|
||||
(3, 1, vec![a.into(), f, j]),
|
||||
(3, 4, vec![z.into(), h, i]),
|
||||
(3, 7, vec![z.into(), g, i]),
|
||||
(2, 0, vec![f, j]),
|
||||
(2, 1, vec![e, j]),
|
||||
(2, 2, vec![h, i]),
|
||||
|
@ -217,26 +227,26 @@ fn small_tree_opening_is_consistent() {
|
|||
|
||||
#[test]
|
||||
fn fail_on_duplicates() {
|
||||
let entries = [(1_u64, int_to_node(1)), (5, int_to_node(2)), (1_u64, int_to_node(3))];
|
||||
let entries = [(1_u64, int_to_leaf(1)), (5, int_to_leaf(2)), (1_u64, int_to_leaf(3))];
|
||||
let smt = SimpleSmt::with_leaves(64, entries);
|
||||
assert!(smt.is_err());
|
||||
|
||||
let entries = [(1_u64, int_to_node(0)), (5, int_to_node(2)), (1_u64, int_to_node(0))];
|
||||
let entries = [(1_u64, int_to_leaf(0)), (5, int_to_leaf(2)), (1_u64, int_to_leaf(0))];
|
||||
let smt = SimpleSmt::with_leaves(64, entries);
|
||||
assert!(smt.is_err());
|
||||
|
||||
let entries = [(1_u64, int_to_node(0)), (5, int_to_node(2)), (1_u64, int_to_node(1))];
|
||||
let entries = [(1_u64, int_to_leaf(0)), (5, int_to_leaf(2)), (1_u64, int_to_leaf(1))];
|
||||
let smt = SimpleSmt::with_leaves(64, entries);
|
||||
assert!(smt.is_err());
|
||||
|
||||
let entries = [(1_u64, int_to_node(1)), (5, int_to_node(2)), (1_u64, int_to_node(0))];
|
||||
let entries = [(1_u64, int_to_leaf(1)), (5, int_to_leaf(2)), (1_u64, int_to_leaf(0))];
|
||||
let smt = SimpleSmt::with_leaves(64, entries);
|
||||
assert!(smt.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_no_duplicates_empty_node() {
|
||||
let entries = [(1_u64, int_to_node(0)), (5, int_to_node(2))];
|
||||
let entries = [(1_u64, int_to_leaf(0)), (5, int_to_leaf(2))];
|
||||
let smt = SimpleSmt::with_leaves(64, entries);
|
||||
assert!(smt.is_ok());
|
||||
}
|
||||
|
@ -244,10 +254,10 @@ fn with_no_duplicates_empty_node() {
|
|||
// HELPER FUNCTIONS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
fn compute_internal_nodes() -> (Word, Word, Word) {
|
||||
let node2 = Rpo256::hash_elements(&[VALUES4[0], VALUES4[1]].concat());
|
||||
let node3 = Rpo256::hash_elements(&[VALUES4[2], VALUES4[3]].concat());
|
||||
fn compute_internal_nodes() -> (RpoDigest, RpoDigest, RpoDigest) {
|
||||
let node2 = Rpo256::merge(&[VALUES4[0], VALUES4[1]]);
|
||||
let node3 = Rpo256::merge(&[VALUES4[2], VALUES4[3]]);
|
||||
let root = Rpo256::merge(&[node2, node3]);
|
||||
|
||||
(root.into(), node2.into(), node3.into())
|
||||
(root, node2, node3)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::{
|
||||
mmr::Mmr, BTreeMap, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, MerklePathSet,
|
||||
MerkleTree, NodeIndex, RootPath, Rpo256, RpoDigest, SimpleSmt, TieredSmt, ValuePath, Vec, Word,
|
||||
mmr::Mmr, BTreeMap, EmptySubtreeRoots, InnerNodeInfo, KvMap, MerkleError, MerklePath,
|
||||
MerklePathSet, MerkleTree, NodeIndex, RecordingMap, RootPath, Rpo256, RpoDigest, SimpleSmt,
|
||||
TieredSmt, ValuePath, Vec,
|
||||
};
|
||||
use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
|
||||
use core::borrow::Borrow;
|
||||
|
@ -8,8 +9,17 @@ use core::borrow::Borrow;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// MERKLE STORE
|
||||
// ================================================================================================
|
||||
|
||||
/// A default [MerkleStore] which uses a simple [BTreeMap] as the backing storage.
|
||||
pub type DefaultMerkleStore = MerkleStore<BTreeMap<RpoDigest, StoreNode>>;
|
||||
|
||||
/// A [MerkleStore] with recording capabilities which uses [RecordingMap] as the backing storage.
|
||||
pub type RecordingMerkleStore = MerkleStore<RecordingMap<RpoDigest, StoreNode>>;
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Node {
|
||||
pub struct StoreNode {
|
||||
left: RpoDigest,
|
||||
right: RpoDigest,
|
||||
}
|
||||
|
@ -42,7 +52,7 @@ pub struct Node {
|
|||
/// # let T1 = MerkleTree::new([A, B, C, D, E, F, G, H1].to_vec()).expect("even number of leaves provided");
|
||||
/// # let ROOT0 = T0.root();
|
||||
/// # let ROOT1 = T1.root();
|
||||
/// let mut store = MerkleStore::new();
|
||||
/// let mut store: MerkleStore = MerkleStore::new();
|
||||
///
|
||||
/// // the store is initialized with the SMT empty nodes
|
||||
/// assert_eq!(store.num_internal_nodes(), 255);
|
||||
|
@ -51,9 +61,8 @@ pub struct Node {
|
|||
/// let tree2 = MerkleTree::new(vec![A, B, C, D, E, F, G, H1]).unwrap();
|
||||
///
|
||||
/// // populates the store with two merkle trees, common nodes are shared
|
||||
/// store
|
||||
/// .extend(tree1.inner_nodes())
|
||||
/// .extend(tree2.inner_nodes());
|
||||
/// store.extend(tree1.inner_nodes());
|
||||
/// store.extend(tree2.inner_nodes());
|
||||
///
|
||||
/// // every leaf except the last are the same
|
||||
/// for i in 0..7 {
|
||||
|
@ -78,40 +87,24 @@ pub struct Node {
|
|||
/// assert_eq!(store.num_internal_nodes() - 255, 10);
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct MerkleStore {
|
||||
nodes: BTreeMap<RpoDigest, Node>,
|
||||
pub struct MerkleStore<T: KvMap<RpoDigest, StoreNode> = BTreeMap<RpoDigest, StoreNode>> {
|
||||
nodes: T,
|
||||
}
|
||||
|
||||
impl Default for MerkleStore {
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> Default for MerkleStore<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl MerkleStore {
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> MerkleStore<T> {
|
||||
// CONSTRUCTORS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Creates an empty `MerkleStore` instance.
|
||||
pub fn new() -> MerkleStore {
|
||||
pub fn new() -> MerkleStore<T> {
|
||||
// pre-populate the store with the empty hashes
|
||||
let subtrees = EmptySubtreeRoots::empty_hashes(255);
|
||||
let nodes = subtrees
|
||||
.iter()
|
||||
.rev()
|
||||
.copied()
|
||||
.zip(subtrees.iter().rev().skip(1).copied())
|
||||
.map(|(child, parent)| {
|
||||
(
|
||||
parent,
|
||||
Node {
|
||||
left: child,
|
||||
right: child,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let nodes = empty_hashes().into_iter().collect();
|
||||
MerkleStore { nodes }
|
||||
}
|
||||
|
||||
|
@ -126,25 +119,24 @@ impl MerkleStore {
|
|||
/// Returns the node at `index` rooted on the tree `root`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method can return the following errors:
|
||||
/// - `RootNotInStore` if the `root` is not present in the store.
|
||||
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in the store.
|
||||
pub fn get_node(&self, root: Word, index: NodeIndex) -> Result<Word, MerkleError> {
|
||||
let mut hash: RpoDigest = root.into();
|
||||
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in
|
||||
/// the store.
|
||||
pub fn get_node(&self, root: RpoDigest, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
|
||||
let mut hash = root;
|
||||
|
||||
// corner case: check the root is in the store when called with index `NodeIndex::root()`
|
||||
self.nodes.get(&hash).ok_or(MerkleError::RootNotInStore(hash.into()))?;
|
||||
self.nodes.get(&hash).ok_or(MerkleError::RootNotInStore(hash))?;
|
||||
|
||||
for i in (0..index.depth()).rev() {
|
||||
let node =
|
||||
self.nodes.get(&hash).ok_or(MerkleError::NodeNotInStore(hash.into(), index))?;
|
||||
let node = self.nodes.get(&hash).ok_or(MerkleError::NodeNotInStore(hash, index))?;
|
||||
|
||||
let bit = (index.value() >> i) & 1;
|
||||
hash = if bit == 0 { node.left } else { node.right }
|
||||
}
|
||||
|
||||
Ok(hash.into())
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
/// Returns the node at the specified `index` and its opening to the `root`.
|
||||
|
@ -154,24 +146,24 @@ impl MerkleStore {
|
|||
/// # Errors
|
||||
/// This method can return the following errors:
|
||||
/// - `RootNotInStore` if the `root` is not present in the store.
|
||||
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in the store.
|
||||
pub fn get_path(&self, root: Word, index: NodeIndex) -> Result<ValuePath, MerkleError> {
|
||||
let mut hash: RpoDigest = root.into();
|
||||
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in
|
||||
/// the store.
|
||||
pub fn get_path(&self, root: RpoDigest, index: NodeIndex) -> Result<ValuePath, MerkleError> {
|
||||
let mut hash = root;
|
||||
let mut path = Vec::with_capacity(index.depth().into());
|
||||
|
||||
// corner case: check the root is in the store when called with index `NodeIndex::root()`
|
||||
self.nodes.get(&hash).ok_or(MerkleError::RootNotInStore(hash.into()))?;
|
||||
self.nodes.get(&hash).ok_or(MerkleError::RootNotInStore(hash))?;
|
||||
|
||||
for i in (0..index.depth()).rev() {
|
||||
let node =
|
||||
self.nodes.get(&hash).ok_or(MerkleError::NodeNotInStore(hash.into(), index))?;
|
||||
let node = self.nodes.get(&hash).ok_or(MerkleError::NodeNotInStore(hash, index))?;
|
||||
|
||||
let bit = (index.value() >> i) & 1;
|
||||
hash = if bit == 0 {
|
||||
path.push(node.right.into());
|
||||
path.push(node.right);
|
||||
node.left
|
||||
} else {
|
||||
path.push(node.left.into());
|
||||
path.push(node.left);
|
||||
node.right
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +172,7 @@ impl MerkleStore {
|
|||
path.reverse();
|
||||
|
||||
Ok(ValuePath {
|
||||
value: hash.into(),
|
||||
value: hash,
|
||||
path: MerklePath::new(path),
|
||||
})
|
||||
}
|
||||
|
@ -202,7 +194,7 @@ impl MerkleStore {
|
|||
/// information, check [NodeIndex::new].
|
||||
pub fn get_leaf_depth(
|
||||
&self,
|
||||
root: Word,
|
||||
root: RpoDigest,
|
||||
tree_depth: u8,
|
||||
index: u64,
|
||||
) -> Result<u8, MerkleError> {
|
||||
|
@ -221,9 +213,9 @@ impl MerkleStore {
|
|||
|
||||
// check if the root exists, providing the proper error report if it doesn't
|
||||
let empty = EmptySubtreeRoots::empty_hashes(tree_depth);
|
||||
let mut hash: RpoDigest = root.into();
|
||||
let mut hash = root;
|
||||
if !self.nodes.contains_key(&hash) {
|
||||
return Err(MerkleError::RootNotInStore(hash.into()));
|
||||
return Err(MerkleError::RootNotInStore(hash));
|
||||
}
|
||||
|
||||
// we traverse from root to leaf, so the path is reversed
|
||||
|
@ -263,14 +255,14 @@ impl MerkleStore {
|
|||
/// nodes which are descendants of the specified roots.
|
||||
///
|
||||
/// The roots for which no descendants exist in this Merkle store are ignored.
|
||||
pub fn subset<I, R>(&self, roots: I) -> MerkleStore
|
||||
pub fn subset<I, R>(&self, roots: I) -> MerkleStore<T>
|
||||
where
|
||||
I: Iterator<Item = R>,
|
||||
R: Borrow<Word>,
|
||||
R: Borrow<RpoDigest>,
|
||||
{
|
||||
let mut store = MerkleStore::new();
|
||||
for root in roots {
|
||||
let root = RpoDigest::from(*root.borrow());
|
||||
let root = *root.borrow();
|
||||
store.clone_tree_from(root, self);
|
||||
}
|
||||
store
|
||||
|
@ -279,32 +271,15 @@ impl MerkleStore {
|
|||
/// Iterator over the inner nodes of the [MerkleStore].
|
||||
pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
|
||||
self.nodes.iter().map(|(r, n)| InnerNodeInfo {
|
||||
value: r.into(),
|
||||
left: n.left.into(),
|
||||
right: n.right.into(),
|
||||
value: *r,
|
||||
left: n.left,
|
||||
right: n.right,
|
||||
})
|
||||
}
|
||||
|
||||
// STATE MUTATORS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Adds a sequence of nodes yielded by the provided iterator into the store.
|
||||
pub fn extend<I>(&mut self, iter: I) -> &mut MerkleStore
|
||||
where
|
||||
I: Iterator<Item = InnerNodeInfo>,
|
||||
{
|
||||
for node in iter {
|
||||
let value: RpoDigest = node.value.into();
|
||||
let left: RpoDigest = node.left.into();
|
||||
let right: RpoDigest = node.right.into();
|
||||
|
||||
debug_assert_eq!(Rpo256::merge(&[left, right]), value);
|
||||
self.nodes.insert(value, Node { left, right });
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds all the nodes of a Merkle path represented by `path`, opening to `node`. Returns the
|
||||
/// new root.
|
||||
///
|
||||
|
@ -313,16 +288,16 @@ impl MerkleStore {
|
|||
pub fn add_merkle_path(
|
||||
&mut self,
|
||||
index: u64,
|
||||
node: Word,
|
||||
node: RpoDigest,
|
||||
path: MerklePath,
|
||||
) -> Result<Word, MerkleError> {
|
||||
let root = path.inner_nodes(index, node)?.fold(Word::default(), |_, node| {
|
||||
let value: RpoDigest = node.value.into();
|
||||
let left: RpoDigest = node.left.into();
|
||||
let right: RpoDigest = node.right.into();
|
||||
) -> Result<RpoDigest, MerkleError> {
|
||||
let root = path.inner_nodes(index, node)?.fold(RpoDigest::default(), |_, node| {
|
||||
let value: RpoDigest = node.value;
|
||||
let left: RpoDigest = node.left;
|
||||
let right: RpoDigest = node.right;
|
||||
|
||||
debug_assert_eq!(Rpo256::merge(&[left, right]), value);
|
||||
self.nodes.insert(value, Node { left, right });
|
||||
self.nodes.insert(value, StoreNode { left, right });
|
||||
|
||||
node.value
|
||||
});
|
||||
|
@ -337,7 +312,7 @@ impl MerkleStore {
|
|||
/// For further reference, check [MerkleStore::add_merkle_path].
|
||||
pub fn add_merkle_paths<I>(&mut self, paths: I) -> Result<(), MerkleError>
|
||||
where
|
||||
I: IntoIterator<Item = (u64, Word, MerklePath)>,
|
||||
I: IntoIterator<Item = (u64, RpoDigest, MerklePath)>,
|
||||
{
|
||||
for (index_value, node, path) in paths.into_iter() {
|
||||
self.add_merkle_path(index_value, node, path)?;
|
||||
|
@ -348,7 +323,10 @@ impl MerkleStore {
|
|||
/// Appends the provided [MerklePathSet] into the store.
|
||||
///
|
||||
/// For further reference, check [MerkleStore::add_merkle_path].
|
||||
pub fn add_merkle_path_set(&mut self, path_set: &MerklePathSet) -> Result<Word, MerkleError> {
|
||||
pub fn add_merkle_path_set(
|
||||
&mut self,
|
||||
path_set: &MerklePathSet,
|
||||
) -> Result<RpoDigest, MerkleError> {
|
||||
let root = path_set.root();
|
||||
for (index, path) in path_set.to_paths() {
|
||||
self.add_merkle_path(index, path.value, path.path)?;
|
||||
|
@ -359,15 +337,15 @@ impl MerkleStore {
|
|||
/// Sets a node to `value`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method can return the following errors:
|
||||
/// - `RootNotInStore` if the `root` is not present in the store.
|
||||
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in the store.
|
||||
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in
|
||||
/// the store.
|
||||
pub fn set_node(
|
||||
&mut self,
|
||||
mut root: Word,
|
||||
mut root: RpoDigest,
|
||||
index: NodeIndex,
|
||||
value: Word,
|
||||
value: RpoDigest,
|
||||
) -> Result<RootPath, MerkleError> {
|
||||
let node = value;
|
||||
let ValuePath { value, path } = self.get_path(root, index)?;
|
||||
|
@ -383,14 +361,29 @@ impl MerkleStore {
|
|||
/// Merges two elements and adds the resulting node into the store.
|
||||
///
|
||||
/// Merges arbitrary values. They may be leafs, nodes, or a mixture of both.
|
||||
pub fn merge_roots(&mut self, root1: Word, root2: Word) -> Result<Word, MerkleError> {
|
||||
let left: RpoDigest = root1.into();
|
||||
let right: RpoDigest = root2.into();
|
||||
pub fn merge_roots(
|
||||
&mut self,
|
||||
left_root: RpoDigest,
|
||||
right_root: RpoDigest,
|
||||
) -> Result<RpoDigest, MerkleError> {
|
||||
let parent = Rpo256::merge(&[left_root, right_root]);
|
||||
self.nodes.insert(
|
||||
parent,
|
||||
StoreNode {
|
||||
left: left_root,
|
||||
right: right_root,
|
||||
},
|
||||
);
|
||||
|
||||
let parent = Rpo256::merge(&[left, right]);
|
||||
self.nodes.insert(parent, Node { left, right });
|
||||
Ok(parent)
|
||||
}
|
||||
|
||||
Ok(parent.into())
|
||||
// DESTRUCTURING
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Returns the inner storage of this MerkleStore while consuming `self`.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.nodes
|
||||
}
|
||||
|
||||
// HELPER METHODS
|
||||
|
@ -404,7 +397,7 @@ impl MerkleStore {
|
|||
if let Some(node) = source.nodes.get(&root) {
|
||||
// if the node has already been inserted, no need to process it further as all of its
|
||||
// descendants should be already cloned from the source store
|
||||
if matches!(self.nodes.insert(root, *node), None) {
|
||||
if self.nodes.insert(root, *node).is_none() {
|
||||
self.clone_tree_from(node.left, source);
|
||||
self.clone_tree_from(node.right, source);
|
||||
}
|
||||
|
@ -415,74 +408,91 @@ impl MerkleStore {
|
|||
// CONVERSIONS
|
||||
// ================================================================================================
|
||||
|
||||
impl From<&MerkleTree> for MerkleStore {
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> From<&MerkleTree> for MerkleStore<T> {
|
||||
fn from(value: &MerkleTree) -> Self {
|
||||
let mut store = MerkleStore::new();
|
||||
store.extend(value.inner_nodes());
|
||||
store
|
||||
let nodes = combine_nodes_with_empty_hashes(value.inner_nodes()).collect();
|
||||
Self { nodes }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SimpleSmt> for MerkleStore {
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> From<&SimpleSmt> for MerkleStore<T> {
|
||||
fn from(value: &SimpleSmt) -> Self {
|
||||
let mut store = MerkleStore::new();
|
||||
store.extend(value.inner_nodes());
|
||||
store
|
||||
let nodes = combine_nodes_with_empty_hashes(value.inner_nodes()).collect();
|
||||
Self { nodes }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Mmr> for MerkleStore {
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> From<&Mmr> for MerkleStore<T> {
|
||||
fn from(value: &Mmr) -> Self {
|
||||
let mut store = MerkleStore::new();
|
||||
store.extend(value.inner_nodes());
|
||||
store
|
||||
let nodes = combine_nodes_with_empty_hashes(value.inner_nodes()).collect();
|
||||
Self { nodes }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TieredSmt> for MerkleStore {
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> From<&TieredSmt> for MerkleStore<T> {
|
||||
fn from(value: &TieredSmt) -> Self {
|
||||
let mut store = MerkleStore::new();
|
||||
store.extend(value.inner_nodes());
|
||||
store
|
||||
let nodes = combine_nodes_with_empty_hashes(value.inner_nodes()).collect();
|
||||
Self { nodes }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<InnerNodeInfo> for MerkleStore {
|
||||
fn from_iter<T: IntoIterator<Item = InnerNodeInfo>>(iter: T) -> Self {
|
||||
let mut store = MerkleStore::new();
|
||||
store.extend(iter.into_iter());
|
||||
store
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> From<T> for MerkleStore<T> {
|
||||
fn from(values: T) -> Self {
|
||||
let nodes = values.into_iter().chain(empty_hashes().into_iter()).collect();
|
||||
Self { nodes }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> FromIterator<InnerNodeInfo> for MerkleStore<T> {
|
||||
fn from_iter<I: IntoIterator<Item = InnerNodeInfo>>(iter: I) -> Self {
|
||||
let nodes = combine_nodes_with_empty_hashes(iter.into_iter()).collect();
|
||||
Self { nodes }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> FromIterator<(RpoDigest, StoreNode)> for MerkleStore<T> {
|
||||
fn from_iter<I: IntoIterator<Item = (RpoDigest, StoreNode)>>(iter: I) -> Self {
|
||||
let nodes = iter.into_iter().chain(empty_hashes().into_iter()).collect();
|
||||
Self { nodes }
|
||||
}
|
||||
}
|
||||
|
||||
// ITERATORS
|
||||
// ================================================================================================
|
||||
|
||||
impl Extend<InnerNodeInfo> for MerkleStore {
|
||||
fn extend<T: IntoIterator<Item = InnerNodeInfo>>(&mut self, iter: T) {
|
||||
self.extend(iter.into_iter());
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> Extend<InnerNodeInfo> for MerkleStore<T> {
|
||||
fn extend<I: IntoIterator<Item = InnerNodeInfo>>(&mut self, iter: I) {
|
||||
self.nodes.extend(iter.into_iter().map(|info| {
|
||||
(
|
||||
info.value,
|
||||
StoreNode {
|
||||
left: info.left,
|
||||
right: info.right,
|
||||
},
|
||||
)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// SERIALIZATION
|
||||
// ================================================================================================
|
||||
|
||||
impl Serializable for Node {
|
||||
impl Serializable for StoreNode {
|
||||
fn write_into<W: ByteWriter>(&self, target: &mut W) {
|
||||
self.left.write_into(target);
|
||||
self.right.write_into(target);
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserializable for Node {
|
||||
impl Deserializable for StoreNode {
|
||||
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
|
||||
let left = RpoDigest::read_from(source)?;
|
||||
let right = RpoDigest::read_from(source)?;
|
||||
Ok(Node { left, right })
|
||||
Ok(StoreNode { left, right })
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for MerkleStore {
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> Serializable for MerkleStore<T> {
|
||||
fn write_into<W: ByteWriter>(&self, target: &mut W) {
|
||||
target.write_u64(self.nodes.len() as u64);
|
||||
|
||||
|
@ -493,17 +503,55 @@ impl Serializable for MerkleStore {
|
|||
}
|
||||
}
|
||||
|
||||
impl Deserializable for MerkleStore {
|
||||
impl<T: KvMap<RpoDigest, StoreNode>> Deserializable for MerkleStore<T> {
|
||||
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
|
||||
let len = source.read_u64()?;
|
||||
let mut nodes: BTreeMap<RpoDigest, Node> = BTreeMap::new();
|
||||
let mut nodes: Vec<(RpoDigest, StoreNode)> = Vec::with_capacity(len as usize);
|
||||
|
||||
for _ in 0..len {
|
||||
let key = RpoDigest::read_from(source)?;
|
||||
let value = Node::read_from(source)?;
|
||||
nodes.insert(key, value);
|
||||
let value = StoreNode::read_from(source)?;
|
||||
nodes.push((key, value));
|
||||
}
|
||||
|
||||
Ok(MerkleStore { nodes })
|
||||
Ok(nodes.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
// HELPER FUNCTIONS
|
||||
// ================================================================================================
|
||||
|
||||
/// Creates empty hashes for all the subtrees of a tree with a max depth of 255.
|
||||
fn empty_hashes() -> impl IntoIterator<Item = (RpoDigest, StoreNode)> {
|
||||
let subtrees = EmptySubtreeRoots::empty_hashes(255);
|
||||
subtrees.iter().rev().copied().zip(subtrees.iter().rev().skip(1).copied()).map(
|
||||
|(child, parent)| {
|
||||
(
|
||||
parent,
|
||||
StoreNode {
|
||||
left: child,
|
||||
right: child,
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Consumes an iterator of [InnerNodeInfo] and returns an iterator of `(value, node)` tuples
|
||||
/// which includes the nodes associate with roots of empty subtrees up to a depth of 255.
|
||||
fn combine_nodes_with_empty_hashes(
|
||||
nodes: impl IntoIterator<Item = InnerNodeInfo>,
|
||||
) -> impl Iterator<Item = (RpoDigest, StoreNode)> {
|
||||
nodes
|
||||
.into_iter()
|
||||
.map(|info| {
|
||||
(
|
||||
info.value,
|
||||
StoreNode {
|
||||
left: info.left,
|
||||
right: info.right,
|
||||
},
|
||||
)
|
||||
})
|
||||
.chain(empty_hashes().into_iter())
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
use super::{
|
||||
super::EMPTY_WORD, Deserializable, EmptySubtreeRoots, MerkleError, MerklePath, MerkleStore,
|
||||
NodeIndex, RpoDigest, Serializable,
|
||||
DefaultMerkleStore as MerkleStore, EmptySubtreeRoots, MerkleError, MerklePath, NodeIndex,
|
||||
RecordingMerkleStore, RpoDigest,
|
||||
};
|
||||
use crate::{
|
||||
hash::rpo::Rpo256,
|
||||
merkle::{int_to_node, MerklePathSet, MerkleTree, SimpleSmt},
|
||||
Felt, Word, WORD_SIZE,
|
||||
merkle::{digests_to_words, int_to_leaf, int_to_node, MerklePathSet, MerkleTree, SimpleSmt},
|
||||
Felt, Word, ONE, WORD_SIZE, ZERO,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use super::{Deserializable, Serializable};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
|
@ -15,9 +18,10 @@ use std::error::Error;
|
|||
// ================================================================================================
|
||||
|
||||
const KEYS4: [u64; 4] = [0, 1, 2, 3];
|
||||
const VALUES4: [Word; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
|
||||
const VALUES4: [RpoDigest; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
|
||||
|
||||
const VALUES8: [Word; 8] = [
|
||||
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),
|
||||
|
@ -33,7 +37,7 @@ const VALUES8: [Word; 8] = [
|
|||
|
||||
#[test]
|
||||
fn test_root_not_in_store() -> Result<(), MerkleError> {
|
||||
let mtree = MerkleTree::new(VALUES4.to_vec())?;
|
||||
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)),
|
||||
|
@ -51,7 +55,7 @@ fn test_root_not_in_store() -> Result<(), MerkleError> {
|
|||
|
||||
#[test]
|
||||
fn test_merkle_tree() -> Result<(), MerkleError> {
|
||||
let mtree = MerkleTree::new(VALUES4.to_vec())?;
|
||||
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
|
||||
let store = MerkleStore::from(&mtree);
|
||||
|
||||
// STORE LEAVES ARE CORRECT -------------------------------------------------------------------
|
||||
|
@ -152,12 +156,12 @@ fn test_merkle_tree() -> Result<(), MerkleError> {
|
|||
#[test]
|
||||
fn test_empty_roots() {
|
||||
let store = MerkleStore::default();
|
||||
let mut root = RpoDigest::new(EMPTY_WORD);
|
||||
let mut root = RpoDigest::default();
|
||||
|
||||
for depth in 0..255 {
|
||||
root = Rpo256::merge(&[root; 2]);
|
||||
assert!(
|
||||
store.get_node(root.into(), NodeIndex::make(0, 0)).is_ok(),
|
||||
store.get_node(root, NodeIndex::make(0, 0)).is_ok(),
|
||||
"The root of the empty tree of depth {depth} must be registered"
|
||||
);
|
||||
}
|
||||
|
@ -176,13 +180,17 @@ fn test_leaf_paths_for_empty_trees() -> Result<(), MerkleError> {
|
|||
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, EMPTY_WORD, "the leaf of an empty tree is always ZERO");
|
||||
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(), EMPTY_WORD).unwrap(),
|
||||
store_path.path.compute_root(depth.into(), RpoDigest::default()).unwrap(),
|
||||
smt.root(),
|
||||
"computed root from the path must match the empty tree root"
|
||||
);
|
||||
|
@ -193,7 +201,8 @@ fn test_leaf_paths_for_empty_trees() -> Result<(), MerkleError> {
|
|||
|
||||
#[test]
|
||||
fn test_get_invalid_node() {
|
||||
let mtree = MerkleTree::new(VALUES4.to_vec()).expect("creating a merkle tree must work");
|
||||
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));
|
||||
}
|
||||
|
@ -201,16 +210,16 @@ fn test_get_invalid_node() {
|
|||
#[test]
|
||||
fn test_add_sparse_merkle_tree_one_level() -> Result<(), MerkleError> {
|
||||
let keys2: [u64; 2] = [0, 1];
|
||||
let leaves2: [Word; 2] = [int_to_node(1), int_to_node(2)];
|
||||
let leaves2: [Word; 2] = [int_to_leaf(1), int_to_leaf(2)];
|
||||
let smt = SimpleSmt::with_leaves(1, keys2.into_iter().zip(leaves2.into_iter())).unwrap();
|
||||
let store = MerkleStore::from(&smt);
|
||||
|
||||
let idx = NodeIndex::make(1, 0);
|
||||
assert_eq!(smt.get_node(idx).unwrap(), leaves2[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]);
|
||||
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(())
|
||||
|
@ -218,8 +227,10 @@ fn test_add_sparse_merkle_tree_one_level() -> Result<(), MerkleError> {
|
|||
|
||||
#[test]
|
||||
fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
|
||||
let smt =
|
||||
SimpleSmt::with_leaves(SimpleSmt::MAX_DEPTH, KEYS4.into_iter().zip(VALUES4.into_iter()))
|
||||
let smt = SimpleSmt::with_leaves(
|
||||
SimpleSmt::MAX_DEPTH,
|
||||
KEYS4.into_iter().zip(digests_to_words(&VALUES4).into_iter()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let store = MerkleStore::from(&smt);
|
||||
|
@ -248,7 +259,7 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
|
|||
);
|
||||
assert_eq!(
|
||||
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 4)),
|
||||
Ok(EMPTY_WORD),
|
||||
Ok(RpoDigest::default()),
|
||||
"unmodified node 4 must be ZERO"
|
||||
);
|
||||
|
||||
|
@ -328,7 +339,8 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
|
|||
|
||||
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 4)).unwrap();
|
||||
assert_eq!(
|
||||
EMPTY_WORD, result.value,
|
||||
RpoDigest::default(),
|
||||
result.value,
|
||||
"Value for merkle path at index 4 must match leaf value"
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -342,7 +354,7 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
|
|||
|
||||
#[test]
|
||||
fn test_add_merkle_paths() -> Result<(), MerkleError> {
|
||||
let mtree = MerkleTree::new(VALUES4.to_vec())?;
|
||||
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
|
||||
|
||||
let i0 = 0;
|
||||
let p0 = mtree.get_path(NodeIndex::make(2, i0)).unwrap();
|
||||
|
@ -477,7 +489,6 @@ fn wont_open_to_different_depth_root() {
|
|||
for depth in (1..=63).rev() {
|
||||
root = Rpo256::merge(&[root, empty[depth]]);
|
||||
}
|
||||
let root = Word::from(root);
|
||||
|
||||
// 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
|
||||
|
@ -505,22 +516,22 @@ fn store_path_opens_from_leaf() {
|
|||
let k = Rpo256::merge(&[e.into(), f.into()]);
|
||||
let l = Rpo256::merge(&[g.into(), h.into()]);
|
||||
|
||||
let m = Rpo256::merge(&[i.into(), j.into()]);
|
||||
let n = Rpo256::merge(&[k.into(), l.into()]);
|
||||
let m = Rpo256::merge(&[i, j]);
|
||||
let n = Rpo256::merge(&[k, l]);
|
||||
|
||||
let root = Rpo256::merge(&[m.into(), n.into()]);
|
||||
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.into(), NodeIndex::make(3, 1)).unwrap().path;
|
||||
let path = store.get_path(root, NodeIndex::make(3, 1)).unwrap().path;
|
||||
|
||||
let expected = MerklePath::new([a.into(), j.into(), n.into()].to_vec());
|
||||
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(VALUES4.to_vec())?;
|
||||
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);
|
||||
|
@ -532,7 +543,7 @@ fn test_set_node() -> Result<(), MerkleError> {
|
|||
|
||||
#[test]
|
||||
fn test_constructors() -> Result<(), MerkleError> {
|
||||
let mtree = MerkleTree::new(VALUES4.to_vec())?;
|
||||
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
|
||||
let store = MerkleStore::from(&mtree);
|
||||
|
||||
let depth = mtree.depth();
|
||||
|
@ -544,7 +555,11 @@ fn test_constructors() -> Result<(), MerkleError> {
|
|||
}
|
||||
|
||||
let depth = 32;
|
||||
let smt = SimpleSmt::with_leaves(depth, KEYS4.into_iter().zip(VALUES4.into_iter())).unwrap();
|
||||
let smt = SimpleSmt::with_leaves(
|
||||
depth,
|
||||
KEYS4.into_iter().zip(digests_to_words(&VALUES4).into_iter()),
|
||||
)
|
||||
.unwrap();
|
||||
let store = MerkleStore::from(&smt);
|
||||
let depth = smt.depth();
|
||||
|
||||
|
@ -590,11 +605,11 @@ 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: Word = EmptySubtreeRoots::empty_hashes(64)[0].into();
|
||||
let root: RpoDigest = EmptySubtreeRoots::empty_hashes(64)[0];
|
||||
|
||||
// insert first node - works as expected
|
||||
let depth = 64;
|
||||
let node = [Felt::new(key); WORD_SIZE];
|
||||
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();
|
||||
|
@ -607,7 +622,7 @@ fn node_path_should_be_truncated_by_midtier_insert() {
|
|||
let key = key ^ (1 << 63);
|
||||
let key = key >> 8;
|
||||
let depth = 56;
|
||||
let node = [Felt::new(key); WORD_SIZE];
|
||||
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();
|
||||
|
@ -626,13 +641,13 @@ fn node_path_should_be_truncated_by_midtier_insert() {
|
|||
#[test]
|
||||
fn get_leaf_depth_works_depth_64() {
|
||||
let mut store = MerkleStore::new();
|
||||
let mut root: Word = EmptySubtreeRoots::empty_hashes(64)[0].into();
|
||||
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 = [Felt::new(k); WORD_SIZE];
|
||||
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
|
||||
|
@ -649,14 +664,14 @@ fn get_leaf_depth_works_depth_64() {
|
|||
#[test]
|
||||
fn get_leaf_depth_works_with_incremental_depth() {
|
||||
let mut store = MerkleStore::new();
|
||||
let mut root: Word = EmptySubtreeRoots::empty_hashes(64)[0].into();
|
||||
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 = [Felt::new(key); WORD_SIZE];
|
||||
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());
|
||||
|
||||
|
@ -665,7 +680,7 @@ fn get_leaf_depth_works_with_incremental_depth() {
|
|||
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 = [Felt::new(key); WORD_SIZE];
|
||||
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());
|
||||
|
||||
|
@ -673,7 +688,7 @@ fn get_leaf_depth_works_with_incremental_depth() {
|
|||
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 = [Felt::new(key); WORD_SIZE];
|
||||
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());
|
||||
|
||||
|
@ -682,7 +697,7 @@ fn get_leaf_depth_works_with_incremental_depth() {
|
|||
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 = [Felt::new(key); WORD_SIZE];
|
||||
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());
|
||||
}
|
||||
|
@ -690,7 +705,7 @@ fn get_leaf_depth_works_with_incremental_depth() {
|
|||
#[test]
|
||||
fn get_leaf_depth_works_with_depth_8() {
|
||||
let mut store = MerkleStore::new();
|
||||
let mut root: Word = EmptySubtreeRoots::empty_hashes(8)[0].into();
|
||||
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;
|
||||
|
@ -700,7 +715,7 @@ fn get_leaf_depth_works_with_depth_8() {
|
|||
|
||||
for k in [a, b, c, d] {
|
||||
let index = NodeIndex::new(8, k).unwrap();
|
||||
let node = [Felt::new(k); WORD_SIZE];
|
||||
let node = RpoDigest::from([Felt::new(k); WORD_SIZE]);
|
||||
root = store.set_node(root, index, node).unwrap().root;
|
||||
}
|
||||
|
||||
|
@ -739,16 +754,16 @@ fn get_leaf_depth_works_with_depth_8() {
|
|||
#[test]
|
||||
fn mstore_subset() {
|
||||
// add a Merkle tree of depth 3 to the store
|
||||
let mtree = MerkleTree::new(VALUES8.to_vec()).unwrap();
|
||||
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(VALUES8[..4].to_vec()).unwrap();
|
||||
let subtree2 = MerkleTree::new(VALUES8[2..4].to_vec()).unwrap();
|
||||
let subtree3 = MerkleTree::new(VALUES8[6..].to_vec()).unwrap();
|
||||
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 ---------------------------------------------
|
||||
|
||||
|
@ -780,7 +795,7 @@ 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);
|
||||
assert_eq!(*path1.value, *value);
|
||||
|
||||
let path2 = subtree.get_path(index).unwrap();
|
||||
assert_eq!(path1.path, path2);
|
||||
|
@ -793,9 +808,60 @@ fn check_mstore_subtree(store: &MerkleStore, subtree: &MerkleTree) {
|
|||
#[cfg(feature = "std")]
|
||||
#[test]
|
||||
fn test_serialization() -> Result<(), Box<dyn Error>> {
|
||||
let mtree = MerkleTree::new(VALUES4.to_vec())?;
|
||||
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.into_proof();
|
||||
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());
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{
|
||||
BTreeMap, BTreeSet, EmptySubtreeRoots, Felt, InnerNodeInfo, MerkleError, MerklePath, NodeIndex,
|
||||
Rpo256, RpoDigest, StarkField, Vec, Word, EMPTY_WORD, ZERO,
|
||||
Rpo256, RpoDigest, StarkField, Vec, Word, ZERO,
|
||||
};
|
||||
use core::cmp;
|
||||
|
||||
|
@ -54,6 +54,9 @@ impl TieredSmt {
|
|||
/// Maximum node depth. This is also the bottom tier of the tree.
|
||||
const MAX_DEPTH: u8 = 64;
|
||||
|
||||
/// Value of an empty leaf.
|
||||
pub const EMPTY_VALUE: Word = super::empty_roots::EMPTY_WORD;
|
||||
|
||||
// CONSTRUCTORS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -74,12 +77,12 @@ impl TieredSmt {
|
|||
let mut empty_entries = BTreeSet::new();
|
||||
for (key, value) in entries {
|
||||
let old_value = tree.insert(key, value);
|
||||
if old_value != EMPTY_WORD || empty_entries.contains(&key) {
|
||||
if old_value != Self::EMPTY_VALUE || empty_entries.contains(&key) {
|
||||
return Err(MerkleError::DuplicateValuesForKey(key));
|
||||
}
|
||||
// if we've processed an empty entry, add the key to the set of empty entry keys, and
|
||||
// if this key was already in the set, return an error
|
||||
if value == EMPTY_WORD && !empty_entries.insert(key) {
|
||||
if value == Self::EMPTY_VALUE && !empty_entries.insert(key) {
|
||||
return Err(MerkleError::DuplicateValuesForKey(key));
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +126,7 @@ impl TieredSmt {
|
|||
let mut path = Vec::with_capacity(index.depth() as usize);
|
||||
for _ in 0..index.depth() {
|
||||
let node = self.get_node_unchecked(&index.sibling());
|
||||
path.push(node.into());
|
||||
path.push(node);
|
||||
index.move_up();
|
||||
}
|
||||
|
||||
|
@ -136,7 +139,7 @@ impl TieredSmt {
|
|||
pub fn get_value(&self, key: RpoDigest) -> Word {
|
||||
match self.values.get(&key) {
|
||||
Some(value) => *value,
|
||||
None => EMPTY_WORD,
|
||||
None => Self::EMPTY_VALUE,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,7 +152,7 @@ impl TieredSmt {
|
|||
/// If the value for the specified key was not previously set, [ZERO; 4] is returned.
|
||||
pub fn insert(&mut self, key: RpoDigest, value: Word) -> Word {
|
||||
// insert the value into the key-value map, and if nothing has changed, return
|
||||
let old_value = self.values.insert(key, value).unwrap_or(EMPTY_WORD);
|
||||
let old_value = self.values.insert(key, value).unwrap_or(Self::EMPTY_VALUE);
|
||||
if old_value == value {
|
||||
return old_value;
|
||||
}
|
||||
|
@ -200,9 +203,9 @@ impl TieredSmt {
|
|||
self.nodes.iter().filter_map(|(index, node)| {
|
||||
if is_inner_node(index) {
|
||||
Some(InnerNodeInfo {
|
||||
value: node.into(),
|
||||
left: self.get_node_unchecked(&index.left_child()).into(),
|
||||
right: self.get_node_unchecked(&index.right_child()).into(),
|
||||
value: *node,
|
||||
left: self.get_node_unchecked(&index.left_child()),
|
||||
right: self.get_node_unchecked(&index.right_child()),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -456,7 +459,7 @@ impl BottomLeaf {
|
|||
let mut elements = Vec::with_capacity(self.values.len() * 2);
|
||||
for (key, val) in self.values.iter() {
|
||||
key.iter().for_each(|&v| elements.push(Felt::new(v)));
|
||||
elements.extend_from_slice(val);
|
||||
elements.extend_from_slice(val.as_slice());
|
||||
}
|
||||
// TODO: hash in domain
|
||||
Rpo256::hash_elements(&elements)
|
||||
|
|
|
@ -17,11 +17,11 @@ fn tsmt_insert_one() {
|
|||
// 16 most significant bits of the key
|
||||
let index = NodeIndex::make(16, raw >> 48);
|
||||
let leaf_node = build_leaf_node(key, value, 16);
|
||||
let tree_root = store.set_node(smt.root().into(), index, leaf_node.into()).unwrap().root;
|
||||
let tree_root = store.set_node(smt.root(), index, leaf_node).unwrap().root;
|
||||
|
||||
smt.insert(key, value);
|
||||
|
||||
assert_eq!(smt.root(), tree_root.into());
|
||||
assert_eq!(smt.root(), tree_root);
|
||||
|
||||
// make sure the value was inserted, and the node is at the expected index
|
||||
assert_eq!(smt.get_value(key), value);
|
||||
|
@ -66,15 +66,15 @@ fn tsmt_insert_two_16() {
|
|||
let mut tree_root = get_init_root();
|
||||
let index_a = NodeIndex::make(32, raw_a >> 32);
|
||||
let leaf_node_a = build_leaf_node(key_a, val_a, 32);
|
||||
tree_root = store.set_node(tree_root, index_a, leaf_node_a.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index_a, leaf_node_a).unwrap().root;
|
||||
|
||||
let index_b = NodeIndex::make(32, raw_b >> 32);
|
||||
let leaf_node_b = build_leaf_node(key_b, val_b, 32);
|
||||
tree_root = store.set_node(tree_root, index_b, leaf_node_b.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index_b, leaf_node_b).unwrap().root;
|
||||
|
||||
// --- verify that data is consistent between store and tree --------------
|
||||
|
||||
assert_eq!(smt.root(), tree_root.into());
|
||||
assert_eq!(smt.root(), tree_root);
|
||||
|
||||
assert_eq!(smt.get_value(key_a), val_a);
|
||||
assert_eq!(smt.get_node(index_a).unwrap(), leaf_node_a);
|
||||
|
@ -122,15 +122,15 @@ fn tsmt_insert_two_32() {
|
|||
let mut tree_root = get_init_root();
|
||||
let index_a = NodeIndex::make(48, raw_a >> 16);
|
||||
let leaf_node_a = build_leaf_node(key_a, val_a, 48);
|
||||
tree_root = store.set_node(tree_root, index_a, leaf_node_a.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index_a, leaf_node_a).unwrap().root;
|
||||
|
||||
let index_b = NodeIndex::make(48, raw_b >> 16);
|
||||
let leaf_node_b = build_leaf_node(key_b, val_b, 48);
|
||||
tree_root = store.set_node(tree_root, index_b, leaf_node_b.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index_b, leaf_node_b).unwrap().root;
|
||||
|
||||
// --- verify that data is consistent between store and tree --------------
|
||||
|
||||
assert_eq!(smt.root(), tree_root.into());
|
||||
assert_eq!(smt.root(), tree_root);
|
||||
|
||||
assert_eq!(smt.get_value(key_a), val_a);
|
||||
assert_eq!(smt.get_node(index_a).unwrap(), leaf_node_a);
|
||||
|
@ -181,19 +181,19 @@ fn tsmt_insert_three() {
|
|||
let mut tree_root = get_init_root();
|
||||
let index_a = NodeIndex::make(32, raw_a >> 32);
|
||||
let leaf_node_a = build_leaf_node(key_a, val_a, 32);
|
||||
tree_root = store.set_node(tree_root, index_a, leaf_node_a.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index_a, leaf_node_a).unwrap().root;
|
||||
|
||||
let index_b = NodeIndex::make(32, raw_b >> 32);
|
||||
let leaf_node_b = build_leaf_node(key_b, val_b, 32);
|
||||
tree_root = store.set_node(tree_root, index_b, leaf_node_b.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index_b, leaf_node_b).unwrap().root;
|
||||
|
||||
let index_c = NodeIndex::make(32, raw_c >> 32);
|
||||
let leaf_node_c = build_leaf_node(key_c, val_c, 32);
|
||||
tree_root = store.set_node(tree_root, index_c, leaf_node_c.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index_c, leaf_node_c).unwrap().root;
|
||||
|
||||
// --- verify that data is consistent between store and tree --------------
|
||||
|
||||
assert_eq!(smt.root(), tree_root.into());
|
||||
assert_eq!(smt.root(), tree_root);
|
||||
|
||||
assert_eq!(smt.get_value(key_a), val_a);
|
||||
assert_eq!(smt.get_node(index_a).unwrap(), leaf_node_a);
|
||||
|
@ -236,9 +236,9 @@ fn tsmt_update() {
|
|||
let mut tree_root = get_init_root();
|
||||
let index = NodeIndex::make(16, raw >> 48);
|
||||
let leaf_node = build_leaf_node(key, value_b, 16);
|
||||
tree_root = store.set_node(tree_root, index, leaf_node.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index, leaf_node).unwrap().root;
|
||||
|
||||
assert_eq!(smt.root(), tree_root.into());
|
||||
assert_eq!(smt.root(), tree_root);
|
||||
|
||||
assert_eq!(smt.get_value(key), value_b);
|
||||
assert_eq!(smt.get_node(index).unwrap(), leaf_node);
|
||||
|
@ -281,11 +281,11 @@ fn tsmt_bottom_tier() {
|
|||
// key_b is smaller than key_a.
|
||||
let leaf_node = build_bottom_leaf_node(&[key_b, key_a], &[val_b, val_a]);
|
||||
let mut tree_root = get_init_root();
|
||||
tree_root = store.set_node(tree_root, index, leaf_node.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index, leaf_node).unwrap().root;
|
||||
|
||||
// --- verify that data is consistent between store and tree --------------
|
||||
|
||||
assert_eq!(smt.root(), tree_root.into());
|
||||
assert_eq!(smt.root(), tree_root);
|
||||
|
||||
assert_eq!(smt.get_value(key_a), val_a);
|
||||
assert_eq!(smt.get_value(key_b), val_b);
|
||||
|
@ -329,15 +329,15 @@ fn tsmt_bottom_tier_two() {
|
|||
let mut tree_root = get_init_root();
|
||||
let index_a = NodeIndex::make(64, raw_a);
|
||||
let leaf_node_a = build_bottom_leaf_node(&[key_a], &[val_a]);
|
||||
tree_root = store.set_node(tree_root, index_a, leaf_node_a.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index_a, leaf_node_a).unwrap().root;
|
||||
|
||||
let index_b = NodeIndex::make(64, raw_b);
|
||||
let leaf_node_b = build_bottom_leaf_node(&[key_b], &[val_b]);
|
||||
tree_root = store.set_node(tree_root, index_b, leaf_node_b.into()).unwrap().root;
|
||||
tree_root = store.set_node(tree_root, index_b, leaf_node_b).unwrap().root;
|
||||
|
||||
// --- verify that data is consistent between store and tree --------------
|
||||
|
||||
assert_eq!(smt.root(), tree_root.into());
|
||||
assert_eq!(smt.root(), tree_root);
|
||||
|
||||
assert_eq!(smt.get_value(key_a), val_a);
|
||||
assert_eq!(smt.get_node(index_a).unwrap(), leaf_node_a);
|
||||
|
@ -406,8 +406,8 @@ fn tsmt_node_not_available() {
|
|||
// HELPER FUNCTIONS
|
||||
// ================================================================================================
|
||||
|
||||
fn get_init_root() -> Word {
|
||||
EmptySubtreeRoots::empty_hashes(64)[0].into()
|
||||
fn get_init_root() -> RpoDigest {
|
||||
EmptySubtreeRoots::empty_hashes(64)[0]
|
||||
}
|
||||
|
||||
fn build_leaf_node(key: RpoDigest, value: Word, depth: u8) -> RpoDigest {
|
||||
|
@ -423,7 +423,7 @@ fn build_bottom_leaf_node(keys: &[RpoDigest], values: &[Word]) -> RpoDigest {
|
|||
let mut key = Word::from(key);
|
||||
key[3] = ZERO;
|
||||
elements.extend_from_slice(&key);
|
||||
elements.extend_from_slice(val);
|
||||
elements.extend_from_slice(val.as_slice());
|
||||
}
|
||||
|
||||
Rpo256::hash_elements(&elements)
|
||||
|
@ -432,10 +432,10 @@ fn build_bottom_leaf_node(keys: &[RpoDigest], values: &[Word]) -> RpoDigest {
|
|||
fn get_non_empty_nodes(store: &MerkleStore) -> Vec<InnerNodeInfo> {
|
||||
store
|
||||
.inner_nodes()
|
||||
.filter(|node| !is_empty_subtree(&RpoDigest::from(node.value)))
|
||||
.filter(|node| !is_empty_subtree(&node.value))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn is_empty_subtree(node: &RpoDigest) -> bool {
|
||||
EmptySubtreeRoots::empty_hashes(255).contains(&node)
|
||||
EmptySubtreeRoots::empty_hashes(255).contains(node)
|
||||
}
|
||||
|
|
21
src/utils.rs
21
src/utils.rs
|
@ -1,21 +0,0 @@
|
|||
use super::Word;
|
||||
use crate::utils::string::String;
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
// RE-EXPORTS
|
||||
// ================================================================================================
|
||||
pub use winter_utils::{
|
||||
collections, string, uninit_vector, ByteReader, ByteWriter, Deserializable,
|
||||
DeserializationError, Serializable, SliceReader,
|
||||
};
|
||||
|
||||
/// Converts a [Word] into hex.
|
||||
pub fn word_to_hex(w: &Word) -> Result<String, fmt::Error> {
|
||||
let mut s = String::new();
|
||||
|
||||
for byte in w.iter().flat_map(|e| e.to_bytes()) {
|
||||
write!(s, "{byte:02x}")?;
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
}
|
324
src/utils/kv_map.rs
Normal file
324
src/utils/kv_map.rs
Normal file
|
@ -0,0 +1,324 @@
|
|||
use core::cell::RefCell;
|
||||
use winter_utils::{
|
||||
collections::{btree_map::IntoIter, BTreeMap, BTreeSet},
|
||||
Box,
|
||||
};
|
||||
|
||||
// KEY-VALUE MAP TRAIT
|
||||
// ================================================================================================
|
||||
|
||||
/// A trait that defines the interface for a key-value map.
|
||||
pub trait KvMap<K: Ord + Clone, V: Clone>:
|
||||
Extend<(K, V)> + FromIterator<(K, V)> + IntoIterator<Item = (K, V)>
|
||||
{
|
||||
fn get(&self, key: &K) -> Option<&V>;
|
||||
fn contains_key(&self, key: &K) -> bool;
|
||||
fn len(&self) -> usize;
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
fn insert(&mut self, key: K, value: V) -> Option<V>;
|
||||
|
||||
fn iter(&self) -> Box<dyn Iterator<Item = (&K, &V)> + '_>;
|
||||
}
|
||||
|
||||
// BTREE MAP `KvMap` IMPLEMENTATION
|
||||
// ================================================================================================
|
||||
|
||||
impl<K: Ord + Clone, V: Clone> KvMap<K, V> for BTreeMap<K, V> {
|
||||
fn get(&self, key: &K) -> Option<&V> {
|
||||
self.get(key)
|
||||
}
|
||||
|
||||
fn contains_key(&self, key: &K) -> bool {
|
||||
self.contains_key(key)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn insert(&mut self, key: K, value: V) -> Option<V> {
|
||||
self.insert(key, value)
|
||||
}
|
||||
|
||||
fn iter(&self) -> Box<dyn Iterator<Item = (&K, &V)> + '_> {
|
||||
Box::new(self.iter())
|
||||
}
|
||||
}
|
||||
|
||||
// RECORDING MAP
|
||||
// ================================================================================================
|
||||
|
||||
/// A [RecordingMap] that records read requests to the underlying key-value map.
|
||||
///
|
||||
/// The data recorder is used to generate a proof for read requests.
|
||||
///
|
||||
/// The [RecordingMap] is composed of three parts:
|
||||
/// - `data`: which contains the current set of key-value pairs in the map.
|
||||
/// - `updates`: which tracks keys for which values have been since the map was instantiated.
|
||||
/// updates include both insertions and updates of values under existing keys.
|
||||
/// - `trace`: which contains the key-value pairs from the original data which have been accesses
|
||||
/// since the map was instantiated.
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||
pub struct RecordingMap<K, V> {
|
||||
data: BTreeMap<K, V>,
|
||||
updates: BTreeSet<K>,
|
||||
trace: RefCell<BTreeMap<K, V>>,
|
||||
}
|
||||
|
||||
impl<K: Ord + Clone, V: Clone> RecordingMap<K, V> {
|
||||
// CONSTRUCTOR
|
||||
// --------------------------------------------------------------------------------------------
|
||||
/// Returns a new [RecordingMap] instance initialized with the provided key-value pairs.
|
||||
/// ([BTreeMap]).
|
||||
pub fn new(init: impl IntoIterator<Item = (K, V)>) -> Self {
|
||||
RecordingMap {
|
||||
data: init.into_iter().collect(),
|
||||
updates: BTreeSet::new(),
|
||||
trace: RefCell::new(BTreeMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
// FINALIZER
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Consumes the [RecordingMap] and returns a [BTreeMap] containing the key-value pairs from
|
||||
/// the initial data set that were read during recording.
|
||||
pub fn into_proof(self) -> BTreeMap<K, V> {
|
||||
self.trace.take()
|
||||
}
|
||||
|
||||
// TEST HELPERS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn trace_len(&self) -> usize {
|
||||
self.trace.borrow().len()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn updates_len(&self) -> usize {
|
||||
self.updates.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Ord + Clone, V: Clone> KvMap<K, V> for RecordingMap<K, V> {
|
||||
// PUBLIC ACCESSORS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Returns a reference to the value associated with the given key if the value exists.
|
||||
///
|
||||
/// If the key is part of the initial data set, the key access is recorded.
|
||||
fn get(&self, key: &K) -> Option<&V> {
|
||||
self.data.get(key).map(|value| {
|
||||
if !self.updates.contains(key) {
|
||||
self.trace.borrow_mut().insert(key.clone(), value.clone());
|
||||
}
|
||||
value
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a boolean to indicate whether the given key exists in the data set.
|
||||
///
|
||||
/// If the key is part of the initial data set, the key access is recorded.
|
||||
fn contains_key(&self, key: &K) -> bool {
|
||||
self.get(key).is_some()
|
||||
}
|
||||
|
||||
/// Returns the number of key-value pairs in the data set.
|
||||
fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
// MUTATORS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Inserts a key-value pair into the data set.
|
||||
///
|
||||
/// If the key already exists in the data set, the value is updated and the old value is
|
||||
/// returned.
|
||||
fn insert(&mut self, key: K, value: V) -> Option<V> {
|
||||
let new_update = self.updates.insert(key.clone());
|
||||
self.data.insert(key.clone(), value).map(|old_value| {
|
||||
if new_update {
|
||||
self.trace.borrow_mut().insert(key, old_value.clone());
|
||||
}
|
||||
old_value
|
||||
})
|
||||
}
|
||||
|
||||
// ITERATION
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Returns an iterator over the key-value pairs in the data set.
|
||||
fn iter(&self) -> Box<dyn Iterator<Item = (&K, &V)> + '_> {
|
||||
Box::new(self.data.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Clone + Ord, V: Clone> Extend<(K, V)> for RecordingMap<K, V> {
|
||||
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(move |(k, v)| {
|
||||
self.insert(k, v);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Clone + Ord, V: Clone> FromIterator<(K, V)> for RecordingMap<K, V> {
|
||||
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
|
||||
Self::new(iter)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Clone + Ord, V: Clone> IntoIterator for RecordingMap<K, V> {
|
||||
type Item = (K, V);
|
||||
type IntoIter = IntoIter<K, V>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.data.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
// TESTS
|
||||
// ================================================================================================
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const ITEMS: [(u64, u64); 5] = [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)];
|
||||
|
||||
#[test]
|
||||
fn test_get_item() {
|
||||
// instantiate a recording map
|
||||
let map = RecordingMap::new(ITEMS.to_vec());
|
||||
|
||||
// get a few items
|
||||
let get_items = [0, 1, 2];
|
||||
for key in get_items.iter() {
|
||||
map.get(key);
|
||||
}
|
||||
|
||||
// convert the map into a proof
|
||||
let proof = map.into_proof();
|
||||
|
||||
// check that the proof contains the expected values
|
||||
for (key, value) in ITEMS.iter() {
|
||||
match get_items.contains(key) {
|
||||
true => assert_eq!(proof.get(key), Some(value)),
|
||||
false => assert_eq!(proof.get(key), None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains_key() {
|
||||
// instantiate a recording map
|
||||
let map = RecordingMap::new(ITEMS.to_vec());
|
||||
|
||||
// check if the map contains a few items
|
||||
let get_items = [0, 1, 2];
|
||||
for key in get_items.iter() {
|
||||
map.contains_key(key);
|
||||
}
|
||||
|
||||
// convert the map into a proof
|
||||
let proof = map.into_proof();
|
||||
|
||||
// check that the proof contains the expected values
|
||||
for (key, _) in ITEMS.iter() {
|
||||
match get_items.contains(key) {
|
||||
true => assert_eq!(proof.contains_key(key), true),
|
||||
false => assert_eq!(proof.contains_key(key), false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len() {
|
||||
// instantiate a recording map
|
||||
let mut map = RecordingMap::new(ITEMS.to_vec());
|
||||
// length of the map should be equal to the number of items
|
||||
assert_eq!(map.len(), ITEMS.len());
|
||||
|
||||
// inserting entry with key that already exists should not change the length, but it does
|
||||
// add entries to the trace and update sets
|
||||
map.insert(4, 5);
|
||||
assert_eq!(map.len(), ITEMS.len());
|
||||
assert_eq!(map.trace_len(), 1);
|
||||
assert_eq!(map.updates_len(), 1);
|
||||
|
||||
// inserting entry with new key should increase the length; it should also record the key
|
||||
// as an updated key, but the trace length does not change since old values were not touched
|
||||
map.insert(5, 5);
|
||||
assert_eq!(map.len(), ITEMS.len() + 1);
|
||||
assert_eq!(map.trace_len(), 1);
|
||||
assert_eq!(map.updates_len(), 2);
|
||||
|
||||
// get some items so that they are saved in the trace; this should record original items
|
||||
// in the trace, but should not affect the set of updates
|
||||
let get_items = [0, 1, 2];
|
||||
for key in get_items.iter() {
|
||||
map.contains_key(key);
|
||||
}
|
||||
assert_eq!(map.trace_len(), 4);
|
||||
assert_eq!(map.updates_len(), 2);
|
||||
|
||||
// read the same items again, this should not have any effect on either length, trace, or
|
||||
// the set of updates
|
||||
let get_items = [0, 1, 2];
|
||||
for key in get_items.iter() {
|
||||
map.contains_key(key);
|
||||
}
|
||||
assert_eq!(map.trace_len(), 4);
|
||||
assert_eq!(map.updates_len(), 2);
|
||||
|
||||
// read a newly inserted item; this should not affect either length, trace, or the set of
|
||||
// updates
|
||||
let _val = map.get(&5).unwrap();
|
||||
assert_eq!(map.trace_len(), 4);
|
||||
assert_eq!(map.updates_len(), 2);
|
||||
|
||||
// update a newly inserted item; this should not affect either length, trace, or the set
|
||||
// of updates
|
||||
map.insert(5, 11);
|
||||
assert_eq!(map.trace_len(), 4);
|
||||
assert_eq!(map.updates_len(), 2);
|
||||
|
||||
// Note: The length reported by the proof will be different to the length originally
|
||||
// reported by the map.
|
||||
let proof = map.into_proof();
|
||||
|
||||
// length of the proof should be equal to get_items + 1. The extra item is the original
|
||||
// value at key = 4u64
|
||||
assert_eq!(proof.len(), get_items.len() + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iter() {
|
||||
let mut map = RecordingMap::new(ITEMS.to_vec());
|
||||
assert!(map.iter().all(|(x, y)| ITEMS.contains(&(*x, *y))));
|
||||
|
||||
// when inserting entry with key that already exists the iterator should return the new value
|
||||
let new_value = 5;
|
||||
map.insert(4, new_value);
|
||||
assert_eq!(map.iter().count(), ITEMS.len());
|
||||
assert!(map.iter().all(|(x, y)| if x == &4 {
|
||||
y == &new_value
|
||||
} else {
|
||||
ITEMS.contains(&(*x, *y))
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_empty() {
|
||||
// instantiate an empty recording map
|
||||
let empty_map: RecordingMap<u64, u64> = RecordingMap::default();
|
||||
assert!(empty_map.is_empty());
|
||||
|
||||
// instantiate a non-empty recording map
|
||||
let map = RecordingMap::new(ITEMS.to_vec());
|
||||
assert!(!map.is_empty());
|
||||
}
|
||||
}
|
36
src/utils/mod.rs
Normal file
36
src/utils/mod.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use super::{utils::string::String, Word};
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub use alloc::format;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::format;
|
||||
|
||||
mod kv_map;
|
||||
|
||||
// RE-EXPORTS
|
||||
// ================================================================================================
|
||||
pub use winter_utils::{
|
||||
string, uninit_vector, Box, ByteReader, ByteWriter, Deserializable, DeserializationError,
|
||||
Serializable, SliceReader,
|
||||
};
|
||||
|
||||
pub mod collections {
|
||||
pub use super::kv_map::*;
|
||||
pub use winter_utils::collections::*;
|
||||
}
|
||||
|
||||
// UTILITY FUNCTIONS
|
||||
// ================================================================================================
|
||||
|
||||
/// Converts a [Word] into hex.
|
||||
pub fn word_to_hex(w: &Word) -> Result<String, fmt::Error> {
|
||||
let mut s = String::new();
|
||||
|
||||
for byte in w.iter().flat_map(|e| e.to_bytes()) {
|
||||
write!(s, "{byte:02x}")?;
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
}
|
Loading…
Add table
Reference in a new issue