use super::{ super::{RpoDigest, Vec, ZERO}, Felt, MmrError, MmrProof, Rpo256, Word, }; #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct MmrPeaks { /// The number of leaves is used to differentiate accumulators that have the same number of /// peaks. This happens because the number of peaks goes up-and-down as the structure is used /// causing existing trees to be merged and new ones to be created. As an example, every time /// the [Mmr] has a power-of-two number of leaves there is a single peak. /// /// Every tree in the [Mmr] forest has a distinct power-of-two size, this means only the right /// most tree can have an odd number of elements (e.g. `1`). Additionally this means that the bits in /// `num_leaves` conveniently encode the size of each individual tree. /// /// Examples: /// /// - With 5 leaves, the binary `0b101`. The number of set bits is equal the number /// of peaks, in this case there are 2 peaks. The 0-indexed least-significant position of /// the bit determines the number of elements of a tree, so the rightmost tree has `2**0` /// elements and the left most has `2**2`. /// - With 12 leaves, the binary is `0b1100`, this case also has 2 peaks, the /// leftmost tree has `2**3=8` elements, and the right most has `2**2=4` elements. num_leaves: usize, /// All the peaks of every tree in the [Mmr] forest. The peaks are always ordered by number of /// 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`. peaks: Vec, } impl MmrPeaks { pub fn new(num_leaves: usize, peaks: Vec) -> Result { if num_leaves.count_ones() as usize != peaks.len() { return Err(MmrError::InvalidPeaks); } Ok(Self { num_leaves, peaks }) } // ACCESSORS // -------------------------------------------------------------------------------------------- /// Returns a count of the [Mmr]'s leaves. pub fn num_leaves(&self) -> usize { self.num_leaves } /// Returns the current peaks of the [Mmr]. pub fn peaks(&self) -> &[RpoDigest] { &self.peaks } /// Hashes the peaks. /// /// The procedure will: /// - Flatten and pad the peaks to a vector of Felts. /// - Hash the vector of Felts. pub fn hash_peaks(&self) -> Word { Rpo256::hash_elements(&self.flatten_and_pad_peaks()).into() } 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) } /// Flattens and pads the peaks to make hashing inside of the Miden VM easier. /// /// The procedure will: /// - Flatten the vector of Words into a vector of Felts. /// - Pad the peaks with ZERO to an even number of words, this removes the need to handle RPO /// padding. /// - Pad the peaks to a minimum length of 16 words, which reduces the constant cost of /// hashing. pub fn flatten_and_pad_peaks(&self) -> Vec { let num_peaks = self.peaks.len(); // To achieve the padding rules above we calculate the length of the final vector. // This is calculated as the number of field elements. Each peak is 4 field elements. // The length is calculated as follows: // - If there are less than 16 peaks, the data is padded to 16 peaks and as such requires // 64 field elements. // - If there are more than 16 peaks and the number of peaks is odd, the data is padded to // an even number of peaks and as such requires `(num_peaks + 1) * 4` field elements. // - If there are more than 16 peaks and the number of peaks is even, the data is not padded // and as such requires `num_peaks * 4` field elements. let len = if num_peaks < 16 { 64 } else if num_peaks % 2 == 1 { (num_peaks + 1) * 4 } else { num_peaks * 4 }; let mut elements = Vec::with_capacity(len); elements.extend_from_slice( &self .peaks .as_slice() .iter() .map(|digest| digest.into()) .collect::>() .concat(), ); elements.resize(len, ZERO); elements } }