Merge pull request #50 from 0xPolygonMiden/next Tracking PR for release v0.1.1
grjte 91497953+grjte@users.noreply.github.com
Mon, 06 Feb 2023 16:30:01 +0000
8 files changed,
72 insertions(+),
7 deletions(-)
A
CHANGELOG.md
@@ -0,0 +1,13 @@
+## 0.1.1 (2023-02-06) + +- Introduced `merge_in_domain` for the RPO hash function, to allow using a specified domain value in the second capacity register when hashing two digests together. +- Added a simple sparse Merkle tree implementation. +- Added re-exports of Winterfell RandomCoin and RandomCoinError. + +## 0.1.0 (2022-12-02) + +- Initial release on crates.io containing the cryptographic primitives used in Miden VM and the Miden Rollup. +- Hash module with the BLAKE3 and Rescue Prime Optimized hash functions. + - BLAKE3 is implemented with 256-bit, 192-bit, or 160-bit output. + - RPO is implemented with 256-bit output. +- Merkle module, with a set of data structures related to Merkle trees, implemented using the RPO hash function.
M
Cargo.toml
→
Cargo.toml
@@ -1,6 +1,6 @@
[package] name = "miden-crypto" -version = "0.1.0" +version = "0.1.1" description="Miden Cryptographic primitives" authors = ["miden contributors"] readme="README.md"
M
src/hash/rpo/mod.rs
→
src/hash/rpo/mod.rs
@@ -294,6 +294,28 @@ pub fn hash_elements<E: FieldElement<BaseField = Felt>>(elements: &[E]) -> RpoDigest {
<Self as ElementHasher>::hash_elements(elements) } + // DOMAIN IDENTIFIER + // -------------------------------------------------------------------------------------------- + + /// Returns a hash of two digests and a domain identifier. + pub fn merge_in_domain(values: &[RpoDigest; 2], domain: Felt) -> RpoDigest { + // initialize the state by copying the digest elements into the rate portion of the state + // (8 total elements), and set the capacity elements to 0. + let mut state = [ZERO; STATE_WIDTH]; + let it = RpoDigest::digests_as_elements(values.iter()); + for (i, v) in it.enumerate() { + state[RATE_RANGE.start + i] = *v; + } + + // set the second capacity element to the domain value. The first capacity element is used + // for padding purposes. + state[CAPACITY_RANGE.start + 1] = domain; + + // apply the RPO permutation and return the first four elements of the state + Self::apply_permutation(&mut state); + RpoDigest::new(state[DIGEST_RANGE].try_into().unwrap()) + } + // RESCUE PERMUTATION // --------------------------------------------------------------------------------------------
M
src/hash/rpo/tests.rs
→
src/hash/rpo/tests.rs
@@ -1,5 +1,6 @@
use super::{ - Felt, FieldElement, Hasher, Rpo256, RpoDigest, StarkField, ALPHA, INV_ALPHA, STATE_WIDTH, ZERO, + Felt, FieldElement, Hasher, Rpo256, RpoDigest, StarkField, ALPHA, INV_ALPHA, ONE, STATE_WIDTH, + ZERO, }; use core::convert::TryInto; use rand_utils::rand_value;@@ -49,6 +50,33 @@
let m_result = Rpo256::merge(&digests); let h_result = Rpo256::hash_elements(&elements); assert_eq!(m_result, h_result); +} + +#[test] +fn merge_vs_merge_in_domain() { + let elements = [Felt::new(rand_value()); 8]; + + let digests: [RpoDigest; 2] = [ + RpoDigest::new(elements[..4].try_into().unwrap()), + RpoDigest::new(elements[4..].try_into().unwrap()), + ]; + let merge_result = Rpo256::merge(&digests); + + // ------------- merge with domain = 0 ---------------------------------------------------------- + + // set domain to ZERO. This should not change the result. + let domain = ZERO; + + let merge_in_domain_result = Rpo256::merge_in_domain(&digests, domain); + assert_eq!(merge_result, merge_in_domain_result); + + // ------------- merge with domain = 1 ---------------------------------------------------------- + + // set domain to ONE. This should change the result. + let domain = ONE; + + let merge_in_domain_result = Rpo256::merge_in_domain(&digests, domain); + assert_ne!(merge_result, merge_in_domain_result); } #[test]
M
src/lib.rs
→
src/lib.rs
@@ -10,6 +10,8 @@
// RE-EXPORTS // ================================================================================================ +pub use winter_crypto::{RandomCoin, RandomCoinError}; + pub use winter_math::{fields::f64::BaseElement as Felt, FieldElement, StarkField}; pub mod utils {
M
src/merkle/merkle_path_set.rs
→
src/merkle/merkle_path_set.rs
@@ -4,7 +4,7 @@ // MERKLE PATH SET
// ================================================================================================ /// A set of Merkle paths. -#[derive(Clone, Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct MerklePathSet { root: Word, total_depth: u32,
M
src/merkle/merkle_tree.rs
→
src/merkle/merkle_tree.rs
@@ -7,7 +7,7 @@ // MERKLE TREE
// ================================================================================================ /// A fully-balanced binary Merkle tree (i.e., a tree where the number of leaves is a power of two). -#[derive(Clone, Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct MerkleTree { nodes: Vec<Word>, }
M
src/merkle/simple_smt/mod.rs
→
src/merkle/simple_smt/mod.rs
@@ -9,7 +9,7 @@
/// A sparse Merkle tree with 63-bit keys and 4-element leaf values, without compaction. /// Manipulation and retrieval of leaves and internal nodes is provided by its internal `Store`. /// The root of the tree is recomputed on each new leaf update. -#[derive(Clone, Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct SimpleSmt { root: Word, depth: u32,@@ -186,7 +186,7 @@ /// A data store for sparse Merkle tree key-value pairs.
/// Leaves and branch nodes are stored separately in B-tree maps, indexed by key and (key, depth) /// respectively. Hashes for blank subtrees at each layer are stored in `empty_hashes`, beginning /// with the root hash of an empty tree, and ending with the zero value of a leaf node. -#[derive(Clone, Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] struct Store { branches: BTreeMap<(u64, u32), BranchNode>, leaves: BTreeMap<u64, Word>,@@ -194,7 +194,7 @@ empty_hashes: Vec<RpoDigest>,
depth: u32, } -#[derive(Clone, Debug, Default)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] struct BranchNode { left: RpoDigest, right: RpoDigest,