Skip to main content

commonware_storage/merkle/
hasher.rs

1//! Shared hasher trait and standard implementation for Merkle-family data structures.
2
3use crate::merkle::{Bagging, Error, Family, Location, Position};
4use alloc::vec::Vec;
5use commonware_cryptography::{Digest, Hasher as CHasher};
6use core::marker::PhantomData;
7
8/// A trait for computing the various digests of a Merkle-family structure.
9///
10/// The type parameter `F` determines which Merkle family (MMR, MMB, etc.) this hasher targets, and
11/// consequently which `Position` and `Location` types appear in method signatures. Default
12/// implementations are provided for all methods except `hash()`.
13pub trait Hasher<F: Family>: Clone + Send + Sync {
14    type Digest: Digest;
15
16    /// Hash an arbitrary sequence of byte slices into a single digest.
17    ///
18    /// The parts are concatenated before hashing (i.e. there is no domain separation between
19    /// parts).
20    fn hash<'a>(&self, parts: impl IntoIterator<Item = &'a [u8]>) -> Self::Digest;
21
22    /// The bagging policy applied when this hasher folds peaks into a root. Only affects root peak
23    /// aggregation; `hash`, `leaf_digest`, and `node_digest` are unaffected.
24    fn root_bagging(&self) -> Bagging;
25
26    /// Computes the digest for a node given its position and the digests of its children.
27    fn node_digest(
28        &self,
29        pos: Position<F>,
30        left: &Self::Digest,
31        right: &Self::Digest,
32    ) -> Self::Digest {
33        self.hash([
34            (*pos).to_be_bytes().as_slice(),
35            left.as_ref(),
36            right.as_ref(),
37        ])
38    }
39
40    /// Computes the digest for a leaf given its position and the element it represents.
41    fn leaf_digest(&self, pos: Position<F>, element: &[u8]) -> Self::Digest {
42        self.hash([(*pos).to_be_bytes().as_slice(), element])
43    }
44
45    /// Compute the digest of a byte slice.
46    fn digest(&self, data: &[u8]) -> Self::Digest {
47        self.hash(core::iter::once(data))
48    }
49
50    /// Folds a peak digest into a running accumulator: `Hash(acc || peak)`.
51    fn fold(&self, acc: &Self::Digest, peak: &Self::Digest) -> Self::Digest {
52        self.hash([acc.as_ref(), peak.as_ref()])
53    }
54
55    /// Computes a root using `inactive_peaks` and the bagging policy carried by `self`.
56    ///
57    /// # Errors
58    ///
59    /// Returns [`Error::InvalidInactivePeaks`] if `inactive_peaks` exceeds the number of
60    /// provided peak digests.
61    fn root<'a, I>(
62        &self,
63        leaves: Location<F>,
64        inactive_peaks: usize,
65        peak_digests: I,
66    ) -> Result<Self::Digest, Error<F>>
67    where
68        I: IntoIterator<Item = &'a Self::Digest>,
69        I::IntoIter: ExactSizeIterator,
70    {
71        let iter = peak_digests.into_iter();
72        let peaks = iter.len();
73        self.root_with_folded_peaks(leaves, inactive_peaks, inactive_peaks, iter)
74            .ok_or(Error::InvalidInactivePeaks {
75                requested: inactive_peaks,
76                peaks,
77            })
78    }
79
80    /// Computes a root from a peak list that may already contain a forward-folded prefix
81    /// accumulator. The bagging policy is read from `self.root_bagging()`.
82    ///
83    /// `inactive_peaks_to_fold` is how many leading entries of `peak_digests` to fold before the
84    /// root bagging step. `committed_inactive_peaks` is the boundary committed into the root. They
85    /// coincide when the caller passes raw peak digests, but diverge when the caller has already
86    /// pre-folded part of the inactive prefix: e.g. a proof commits 5 inactive peaks, an outer
87    /// transform collapses the first 3 into a leading accumulator, so the hasher gets `to_fold = 5
88    /// - 3 + 1 = 3` while `committed = 5`.
89    ///
90    /// Returns `None` if `inactive_peaks_to_fold` exceeds the number of provided peak digests, or
91    /// if a nonzero inactive boundary is requested for an empty tree.
92    fn root_with_folded_peaks<'a>(
93        &self,
94        leaves: Location<F>,
95        inactive_peaks_to_fold: usize,
96        committed_inactive_peaks: usize,
97        peak_digests: impl IntoIterator<Item = &'a Self::Digest>,
98    ) -> Option<Self::Digest> {
99        let mut peak_digests = peak_digests.into_iter();
100        let Some(first) = peak_digests.next() else {
101            return (inactive_peaks_to_fold == 0 && committed_inactive_peaks == 0)
102                .then(|| self.digest(&(*leaves).to_be_bytes()));
103        };
104
105        let mut acc = *first;
106        for _ in 0..inactive_peaks_to_fold.saturating_sub(1) {
107            let peak = peak_digests.next()?;
108            acc = self.fold(&acc, peak);
109        }
110
111        let folded_peaks = match self.root_bagging() {
112            Bagging::ForwardFold => {
113                for peak in peak_digests {
114                    acc = self.fold(&acc, peak);
115                }
116                acc
117            }
118            Bagging::BackwardFold => {
119                let (lower, upper) = peak_digests.size_hint();
120                let mut active_peaks = Vec::with_capacity(1 + upper.unwrap_or(lower));
121                active_peaks.push(acc);
122                active_peaks.extend(peak_digests.copied());
123
124                let mut acc = *active_peaks.last().unwrap();
125                for peak in active_peaks.iter().rev().skip(1) {
126                    acc = self.fold(peak, &acc);
127                }
128                acc
129            }
130        };
131
132        if committed_inactive_peaks == 0 {
133            Some(self.hash([(*leaves).to_be_bytes().as_slice(), folded_peaks.as_ref()]))
134        } else {
135            Some(self.hash([
136                (*leaves).to_be_bytes().as_slice(),
137                (committed_inactive_peaks as u64).to_be_bytes().as_slice(),
138                folded_peaks.as_ref(),
139            ]))
140        }
141    }
142}
143
144/// The standard hasher for Merkle-family structures. Leverages no external data.
145///
146/// A single `Standard<H>` implements `Hasher<F>` for every Merkle family `F`, so
147/// one instance can be used with MMR, MMB, or any future family.
148///
149/// The `bagging` field selects how peaks are folded into the root.
150#[derive(Clone)]
151pub struct Standard<H: CHasher> {
152    _hasher: PhantomData<H>,
153    bagging: Bagging,
154}
155
156impl<H: CHasher> Standard<H> {
157    /// Creates a new [Standard] hasher with the given bagging policy.
158    pub const fn new(bagging: Bagging) -> Self {
159        Self {
160            _hasher: PhantomData,
161            bagging,
162        }
163    }
164
165    /// Return the bagging policy used when folding peaks into a root.
166    pub const fn root_bagging(&self) -> Bagging {
167        self.bagging
168    }
169
170    /// Hash an arbitrary sequence of byte slices into a single digest.
171    pub fn hash<'a>(&self, parts: impl IntoIterator<Item = &'a [u8]>) -> H::Digest {
172        let mut h = H::new();
173        for part in parts {
174            h.update(part);
175        }
176        h.finalize()
177    }
178
179    /// Compute the digest of a byte slice.
180    pub fn digest(&self, data: &[u8]) -> H::Digest {
181        self.hash(core::iter::once(data))
182    }
183}
184
185impl<F: Family, H: CHasher> Hasher<F> for Standard<H> {
186    type Digest = H::Digest;
187
188    fn hash<'a>(&self, parts: impl IntoIterator<Item = &'a [u8]>) -> H::Digest {
189        Self::hash(self, parts)
190    }
191
192    fn root_bagging(&self) -> Bagging {
193        Self::root_bagging(self)
194    }
195}
196
197// Forward every method, not just the required ones: a `&T` bound must resolve to `T`'s
198// overrides (e.g. a grafting hasher's `node_digest`), never to the trait defaults.
199impl<F: Family, T: Hasher<F>> Hasher<F> for &T {
200    type Digest = T::Digest;
201
202    fn hash<'a>(&self, parts: impl IntoIterator<Item = &'a [u8]>) -> Self::Digest {
203        (**self).hash(parts)
204    }
205
206    fn root_bagging(&self) -> Bagging {
207        (**self).root_bagging()
208    }
209
210    fn node_digest(
211        &self,
212        pos: Position<F>,
213        left: &Self::Digest,
214        right: &Self::Digest,
215    ) -> Self::Digest {
216        (**self).node_digest(pos, left, right)
217    }
218
219    fn leaf_digest(&self, pos: Position<F>, element: &[u8]) -> Self::Digest {
220        (**self).leaf_digest(pos, element)
221    }
222
223    fn digest(&self, data: &[u8]) -> Self::Digest {
224        (**self).digest(data)
225    }
226
227    fn fold(&self, acc: &Self::Digest, peak: &Self::Digest) -> Self::Digest {
228        (**self).fold(acc, peak)
229    }
230
231    fn root<'a, I>(
232        &self,
233        leaves: Location<F>,
234        inactive_peaks: usize,
235        peak_digests: I,
236    ) -> Result<Self::Digest, Error<F>>
237    where
238        I: IntoIterator<Item = &'a Self::Digest>,
239        I::IntoIter: ExactSizeIterator,
240    {
241        (**self).root(leaves, inactive_peaks, peak_digests)
242    }
243
244    fn root_with_folded_peaks<'a>(
245        &self,
246        leaves: Location<F>,
247        inactive_peaks_to_fold: usize,
248        committed_inactive_peaks: usize,
249        peak_digests: impl IntoIterator<Item = &'a Self::Digest>,
250    ) -> Option<Self::Digest> {
251        (**self).root_with_folded_peaks(
252            leaves,
253            inactive_peaks_to_fold,
254            committed_inactive_peaks,
255            peak_digests,
256        )
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263    use crate::merkle::{
264        mmr::{Location, Position, StandardHasher as Standard},
265        Bagging::{BackwardFold, ForwardFold},
266    };
267    use alloc::vec::Vec;
268    use commonware_cryptography::{sha256, Hasher as CHasher, Sha256};
269
270    #[test]
271    fn test_leaf_digest_sha256() {
272        test_leaf_digest::<Sha256>();
273    }
274
275    #[test]
276    fn test_node_digest_sha256() {
277        test_node_digest::<Sha256>();
278    }
279
280    #[test]
281    fn test_root_sha256() {
282        test_root::<Sha256>();
283    }
284
285    #[test]
286    fn test_invalid_inactive_prefix_returns_err() {
287        let mmr_hasher: Standard<Sha256> = Standard::new(BackwardFold);
288        let d1 = test_digest::<Sha256>(1);
289        let d2 = test_digest::<Sha256>(2);
290        let digests = [d1, d2];
291
292        assert!(matches!(
293            <Standard<Sha256> as Hasher<crate::merkle::mmr::Family>>::root(
294                &mmr_hasher,
295                Location::new(2),
296                3,
297                digests.iter()
298            ),
299            Err(crate::merkle::Error::InvalidInactivePeaks {
300                requested: 3,
301                peaks: 2
302            })
303        ));
304        assert!(
305            <Standard<Sha256> as Hasher<crate::merkle::mmr::Family>>::root_with_folded_peaks(
306                &mmr_hasher,
307                Location::new(2),
308                3,
309                3,
310                digests.iter()
311            )
312            .is_none()
313        );
314        assert!(matches!(
315            <Standard<Sha256> as Hasher<crate::merkle::mmr::Family>>::root(
316                &mmr_hasher,
317                Location::new(0),
318                1,
319                Vec::<sha256::Digest>::new().iter()
320            ),
321            Err(crate::merkle::Error::InvalidInactivePeaks {
322                requested: 1,
323                peaks: 0
324            })
325        ));
326    }
327
328    fn test_digest<H: CHasher>(value: u8) -> H::Digest {
329        H::hash(&[value])
330    }
331
332    fn test_leaf_digest<H: CHasher>() {
333        let mmr_hasher: Standard<H> = Standard::new(ForwardFold);
334        let digest1 = test_digest::<H>(1);
335        let digest2 = test_digest::<H>(2);
336
337        let out = mmr_hasher.leaf_digest(Position::new(0), &digest1);
338        assert_ne!(out, test_digest::<H>(0), "hash should be non-zero");
339
340        let mut out2 = mmr_hasher.leaf_digest(Position::new(0), &digest1);
341        assert_eq!(out, out2, "hash should be re-computed consistently");
342
343        out2 = mmr_hasher.leaf_digest(Position::new(1), &digest1);
344        assert_ne!(out, out2, "hash should change with different pos");
345
346        out2 = mmr_hasher.leaf_digest(Position::new(0), &digest2);
347        assert_ne!(out, out2, "hash should change with different input digest");
348    }
349
350    fn test_node_digest<H: CHasher>() {
351        let mmr_hasher: Standard<H> = Standard::new(ForwardFold);
352
353        let d1 = test_digest::<H>(1);
354        let d2 = test_digest::<H>(2);
355        let d3 = test_digest::<H>(3);
356
357        let out = mmr_hasher.node_digest(Position::new(0), &d1, &d2);
358        assert_ne!(out, test_digest::<H>(0), "hash should be non-zero");
359
360        let mut out2 = mmr_hasher.node_digest(Position::new(0), &d1, &d2);
361        assert_eq!(out, out2, "hash should be re-computed consistently");
362
363        out2 = mmr_hasher.node_digest(Position::new(1), &d1, &d2);
364        assert_ne!(out, out2, "hash should change with different pos");
365
366        out2 = mmr_hasher.node_digest(Position::new(0), &d3, &d2);
367        assert_ne!(
368            out, out2,
369            "hash should change with different first input hash"
370        );
371
372        out2 = mmr_hasher.node_digest(Position::new(0), &d1, &d3);
373        assert_ne!(
374            out, out2,
375            "hash should change with different second input hash"
376        );
377
378        out2 = mmr_hasher.node_digest(Position::new(0), &d2, &d1);
379        assert_ne!(
380            out, out2,
381            "hash should change when swapping order of inputs"
382        );
383    }
384
385    fn test_root<H: CHasher>() {
386        let mmr_hasher: Standard<H> = Standard::new(ForwardFold);
387        let d1 = test_digest::<H>(1);
388        let d2 = test_digest::<H>(2);
389        let d3 = test_digest::<H>(3);
390        let d4 = test_digest::<H>(4);
391
392        let empty_vec: Vec<H::Digest> = Vec::new();
393        let empty_out = mmr_hasher
394            .root(Location::new(0), 0, empty_vec.iter())
395            .expect("zero inactive peaks is always valid");
396        assert_ne!(
397            empty_out,
398            test_digest::<H>(0),
399            "root of empty MMR should be non-zero"
400        );
401        // Empty root is deterministic.
402        assert_eq!(
403            empty_out,
404            mmr_hasher
405                .root(Location::new(0), 0, empty_vec.iter())
406                .expect("zero inactive peaks is always valid")
407        );
408
409        let digests = [d1, d2, d3, d4];
410        let out = mmr_hasher
411            .root(Location::new(10), 0, digests.iter())
412            .expect("zero inactive peaks is always valid");
413        assert_ne!(out, test_digest::<H>(0), "root should be non-zero");
414        assert_ne!(out, empty_out, "root should differ from empty MMR");
415
416        let mut out2 = mmr_hasher
417            .root(Location::new(10), 0, digests.iter())
418            .expect("zero inactive peaks is always valid");
419        assert_eq!(out, out2, "root should be computed consistently");
420
421        out2 = mmr_hasher
422            .root(Location::new(11), 0, digests.iter())
423            .expect("zero inactive peaks is always valid");
424        assert_ne!(out, out2, "root should change with different position");
425
426        let digests = [d1, d2, d4, d3];
427        out2 = mmr_hasher
428            .root(Location::new(10), 0, digests.iter())
429            .expect("zero inactive peaks is always valid");
430        assert_ne!(out, out2, "root should change with different digest order");
431
432        let digests = [d1, d2, d3];
433        out2 = mmr_hasher
434            .root(Location::new(10), 0, digests.iter())
435            .expect("zero inactive peaks is always valid");
436        assert_ne!(
437            out, out2,
438            "root should change with different number of hashes"
439        );
440    }
441}