diff --git a/CHANGELOG.md b/CHANGELOG.md index 4612b0c..7192bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.1 (TBD) + +* Added `Serializable` and `Deserializable` implementations for `PartialMmr` and `InOrderIndex` (#329). + ## 0.10.0 (2024-08-06) * Added more `RpoDigest` and `RpxDigest` conversions (#311). diff --git a/src/merkle/mmr/inorder.rs b/src/merkle/mmr/inorder.rs index 655f364..37b4884 100644 --- a/src/merkle/mmr/inorder.rs +++ b/src/merkle/mmr/inorder.rs @@ -6,6 +6,8 @@ //! leaves count. use core::num::NonZeroUsize; +use winter_utils::{Deserializable, Serializable}; + // IN-ORDER INDEX // ================================================================================================ @@ -112,6 +114,21 @@ impl InOrderIndex { } } +impl Serializable for InOrderIndex { + fn write_into(&self, target: &mut W) { + target.write_usize(self.idx); + } +} + +impl Deserializable for InOrderIndex { + fn read_from( + source: &mut R, + ) -> Result { + let idx = source.read_usize()?; + Ok(InOrderIndex { idx }) + } +} + // CONVERSIONS FROM IN-ORDER INDEX // ------------------------------------------------------------------------------------------------ @@ -127,6 +144,7 @@ impl From for u64 { #[cfg(test)] mod test { use proptest::prelude::*; + use winter_utils::{Deserializable, Serializable}; use super::InOrderIndex; @@ -162,4 +180,12 @@ mod test { assert_eq!(left.sibling(), right); assert_eq!(left, right.sibling()); } + + #[test] + fn test_inorder_index_serialization() { + let index = InOrderIndex::from_leaf_pos(5); + let bytes = index.to_bytes(); + let index2 = InOrderIndex::read_from_bytes(&bytes).unwrap(); + assert_eq!(index, index2); + } } diff --git a/src/merkle/mmr/partial.rs b/src/merkle/mmr/partial.rs index c2c4464..6739c3c 100644 --- a/src/merkle/mmr/partial.rs +++ b/src/merkle/mmr/partial.rs @@ -1,12 +1,15 @@ +use alloc::{ + collections::{BTreeMap, BTreeSet}, + vec::Vec, +}; + +use winter_utils::{Deserializable, Serializable}; + use super::{MmrDelta, MmrProof, Rpo256, RpoDigest}; use crate::merkle::{ mmr::{leaf_to_corresponding_tree, nodes_in_forest}, InOrderIndex, InnerNodeInfo, MerklePath, MmrError, MmrPeaks, }; -use alloc::{ - collections::{BTreeMap, BTreeSet}, - vec::Vec, -}; // TYPE ALIASES // ================================================================================================ @@ -572,6 +575,28 @@ impl<'a, I: Iterator> Iterator for InnerNodeIterator< } } +impl Serializable for PartialMmr { + fn write_into(&self, target: &mut W) { + self.forest.write_into(target); + self.peaks.write_into(target); + self.nodes.write_into(target); + target.write_bool(self.track_latest); + } +} + +impl Deserializable for PartialMmr { + fn read_from( + source: &mut R, + ) -> Result { + let forest = usize::read_from(source)?; + let peaks = Vec::::read_from(source)?; + let nodes = NodeMap::read_from(source)?; + let track_latest = source.read_bool()?; + + Ok(Self { forest, peaks, nodes, track_latest }) + } +} + // UTILS // ================================================================================================ @@ -613,12 +638,15 @@ fn forest_to_rightmost_index(forest: usize) -> InOrderIndex { #[cfg(test)] mod tests { + use alloc::{collections::BTreeSet, vec::Vec}; + + use winter_utils::{Deserializable, Serializable}; + use super::{ forest_to_rightmost_index, forest_to_root_index, InOrderIndex, MmrPeaks, PartialMmr, RpoDigest, }; use crate::merkle::{int_to_node, MerkleStore, Mmr, NodeIndex}; - use alloc::{collections::BTreeSet, vec::Vec}; const LEAVES: [RpoDigest; 7] = [ int_to_node(0), @@ -907,4 +935,16 @@ mod tests { // the openings should be the same assert_eq!(mmr.open(5, mmr.forest()).unwrap(), partial_mmr.open(5).unwrap().unwrap()); } + + #[test] + fn test_partial_mmr_serialization() { + let mmr = Mmr::from((0..7).map(int_to_node)); + let forest_size = mmr.forest(); + let partial_mmr = PartialMmr::from_peaks(mmr.peaks(forest_size).unwrap()); + + let bytes = partial_mmr.to_bytes(); + let decoded = PartialMmr::read_from_bytes(&bytes).unwrap(); + + assert_eq!(partial_mmr, decoded); + } }