Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking PR for v0.6.0 release #154

Merged
merged 23 commits into from
Jun 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
59f7723
chore: update crete version to v0.6.0
bobbinth May 26, 2023
23f448f
feat: partial Merkle tree
Fumuran May 30, 2023
b432447
feat: change constructor from with_leaves to with_paths
Fumuran Jun 1, 2023
ebf71c2
refactor: optimize code, remove not momentarily necessary functions
Fumuran Jun 2, 2023
55cc71d
fix: fix add_path func leaf determination
Fumuran Jun 3, 2023
43f1a4c
refactor: MerkleStore clippy fix
Fumuran Jun 3, 2023
2708a23
refactor: optimize code, fix bugs
Fumuran Jun 5, 2023
218a64b
refactor: small fixes
Fumuran Jun 5, 2023
766702e
refactor: improve tests, small fixes
Fumuran Jun 9, 2023
fe9aa8c
refactor: refactor crypto APIs to use RpoDigest instead of Word
tohrnii Jun 9, 2023
0e0a3fd
refactor: refactor to clean up and simplify things
tohrnii Jun 13, 2023
b9def61
refactor: improve tests, add error tests
Fumuran Jun 13, 2023
049ae32
chore: clean up test code
bobbinth Jun 13, 2023
1be64fc
Merge pull request #157 from 0xPolygonMiden/tohrnii-digest
bobbinth Jun 13, 2023
53d52b8
Merge pull request #156 from 0xPolygonMiden/andrew-partial-mt
bobbinth Jun 14, 2023
fe5cac9
fix: compilation errors
bobbinth Jun 14, 2023
4215e83
feat: add EMPTY_VALUE const to SMTs
bobbinth Jun 14, 2023
cede2e5
Merge pull request #161 from 0xPolygonMiden/bobbin-smt-empty-value
bobbinth Jun 14, 2023
679a30e
feat: introduce recorder objects
frisitano Jun 16, 2023
f08644e
refactor: simplify recording MerkleStore structure
bobbinth Jun 24, 2023
f52ac29
Merge pull request #162 from 0xPolygonMiden/frisitano-tx-executor
bobbinth Jun 24, 2023
b2d6866
refactor: rename Merkle store Node into StoreNode
bobbinth Jun 25, 2023
858f95d
chore: update changelog
bobbinth Jun 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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).
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
6 changes: 3 additions & 3 deletions benches/store.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 8 additions & 5 deletions src/hash/rpo/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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]
Expand All @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion src/merkle/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down
77 changes: 42 additions & 35 deletions src/merkle/merkle_tree.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {
Expand All @@ -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.
Expand All @@ -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 })
Expand All @@ -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]
}

Expand All @@ -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() {
Expand Down Expand Up @@ -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].
Expand Down Expand Up @@ -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;
}

Expand All @@ -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,
}

Expand Down Expand Up @@ -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),
Expand All @@ -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
Expand All @@ -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());
Expand All @@ -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();

Expand All @@ -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();

Expand All @@ -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();

Expand All @@ -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))?;
Expand Down Expand Up @@ -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
Expand All @@ -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)
}
}
19 changes: 15 additions & 4 deletions src/merkle/mmr/accumulator.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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 {
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
}
Expand Down
Loading