Skip to main content

miden_crypto/merkle/smt/partial/serialization/
mod.rs

1//! This module contains a system for producing compact serialized representations of the
2//! `PartialSmt` data structure, intended to reduce data sent over the wire through de-duplication.
3
4pub mod property_tests;
5mod tests;
6
7use alloc::{string::ToString, vec::Vec};
8use core::mem::size_of;
9
10use miden_field::{Felt, FeltFromIntError, Word};
11use miden_serde_utils::{
12    ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
13};
14
15use crate::{
16    Map,
17    merkle::{
18        EmptySubtreeRoots,
19        smt::{SMT_DEPTH, SmtLeaf},
20    },
21};
22
23// UNIQUE NODES
24// ================================================================================================
25
26/// A representation of a partial SMT that contains only the unique nodes in the tree, designed for
27/// better efficiency when sending data across the wire.
28///
29/// It _explicitly_ does not need to contain a fully-realized SMT, and instead may contain some
30/// subset of a full tree. It contains the minimal set of data necessary to reconstruct its input.
31///
32/// # Versioning
33///
34/// Note that this structure is explicitly **not intended to be versioned**. This structure should
35/// be used as part of a broader serialization solution that does include this if necessary.
36///
37/// # Serialization
38///
39/// The serialization and deserialization process does not validate that node or leaf indices are
40/// valid for their level. This is the responsibility of the client of this type.
41#[derive(Clone, Debug, Eq, PartialEq)]
42pub struct UniqueNodes {
43    /// The expected root of the tree after reconstruction.
44    ///
45    /// This primarily exists as a sanity check, taking little extra space but ensuring that we can
46    /// detect more possible cases of corruption.
47    pub root: Word,
48
49    /// The nodes that make up the tree itself.
50    ///
51    /// It maps the node depth to a vector containing all the nodes at that depth, ensuring that no
52    /// data that can be reasonably reconstructed is stored.
53    pub nodes: Map<u8, Vec<(u64, NodeValue)>>,
54
55    /// The leaves of the tree.
56    ///
57    /// It only stores the populated leaves, keyed on their index.
58    pub leaves: Vec<(u64, SmtLeaf)>,
59
60    /// The leaves for which we only have the hash value, and not the actual leaf value.
61    ///
62    /// We keep these separately to the `leaves` as storing them this way is more compact.
63    pub value_only_leaves: Vec<(u64, Word)>,
64}
65
66impl UniqueNodes {
67    /// Creates an empty `UniqueNodes` with no nodes or leaves in it.
68    pub fn empty() -> Self {
69        Self {
70            root: *EmptySubtreeRoots::entry(SMT_DEPTH, 0),
71            nodes: Map::default(),
72            leaves: Vec::default(),
73            value_only_leaves: Vec::default(),
74        }
75    }
76}
77
78impl Default for UniqueNodes {
79    fn default() -> Self {
80        Self::empty()
81    }
82}
83
84impl Serializable for UniqueNodes {
85    fn write_into<W: ByteWriter>(&self, target: &mut W) {
86        // First we write the expected root into the buffer.
87        self.root.write_into(target);
88
89        // We write the length as u64 to ensure portability.
90        let node_count = self.nodes.len() as u64;
91        target.write(node_count);
92
93        // We then write each of the pairs of (u8, Vec<...>) independently.
94        for (depth, nodes) in self.nodes.iter() {
95            target.write(depth);
96            let node_count = nodes.len() as u64;
97            target.write(node_count);
98            target.write_many(nodes.iter());
99        }
100
101        // The leaves themselves come next.
102        let leaf_count = self.leaves.len() as u64;
103        target.write(leaf_count);
104        target.write_many(self.leaves.iter());
105
106        // And the value-only leaves bring up the rear.
107        let value_only_leaf_count = self.value_only_leaves.len() as u64;
108        target.write(value_only_leaf_count);
109        target.write_many(self.value_only_leaves.iter());
110    }
111}
112
113impl Deserializable for UniqueNodes {
114    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
115        // The first item is the 32 bytes containing the expected root of the tree after
116        // reconstruction.
117        let root = Word::read_from(source)?;
118
119        // We first have to read the count of levels.
120        let level_count = source.read_u64()?;
121        let mut nodes = Map::new();
122
123        // Next we have that many levels to read, but each is of a variable size.
124        for _ in 0..level_count {
125            let depth = source.read_u8()?;
126            let node_count = source.read_u64()?;
127            let level_nodes = source
128                .read_many_iter(node_count.try_into().map_err(|_| {
129                    DeserializationError::InvalidValue(format!("Node count {node_count} overflow"))
130                })?)?
131                .collect::<Result<Vec<_>, _>>()?;
132            nodes.insert(depth, level_nodes);
133        }
134
135        // Next we need the number of leaves.
136        let leaf_count = source.read_u64()?;
137        let mut leaves = Vec::new();
138
139        // And then we have to read that many leaves.
140        for _ in 0..leaf_count {
141            leaves.push(source.read()?);
142        }
143
144        // Finally we read the number of value-only leaves...
145        let value_only_leaf_count = source.read_u64()?;
146        let mut value_only_leaves = Vec::new();
147
148        // ... and read that many.
149        for _ in 0..value_only_leaf_count {
150            value_only_leaves.push(source.read()?);
151        }
152
153        Ok(Self { root, nodes, leaves, value_only_leaves })
154    }
155}
156
157// NODE VALUE
158// ================================================================================================
159
160/// The value of a node in the serialized representation.
161///
162/// # Serialization
163///
164/// This enum can be in one of two cases: empty, or containing a Word. The naïve serialization would
165/// use a flag to indicate the variant, costing at least a byte to avoid the need for potentially
166/// expensive unaligned accesses.
167///
168/// [`Word`], however, consists of four `Felt`s, each of which occupies the Goldilocks field. This
169/// provides a niche in each of those `Felt`s that allows us to not require the extra byte when
170/// serializing a true value. As we assume that real values are more common than empty subtree roots
171/// by their very nature, making an empty root take 8 bytes instead of 1 is a smaller cost to pay
172/// than an extra byte for each populated node.
173///
174/// To that end, we encode the type as follows:
175///
176/// - For `Self::EmptySubtreeRoot` we encode the LE bytes for [`u64::MAX`], which exceeds the field
177///   order and hence serves as a sentinel value using the niche.
178/// - For `Self::Present` we simply encode the word as its four component felts. As none of the
179///   felts can take a value exceeding [`Felt::ORDER`], we can immediately disambiguate between this
180///   case and the one above.
181#[derive(Clone, Debug, Eq, PartialEq)]
182pub enum NodeValue {
183    /// The node is the head of an empty subtree at the depth given by the outer map in
184    /// [`UniqueNodes`].
185    EmptySubtreeRoot,
186
187    /// The node's value is the provided hash.
188    Present(Word),
189}
190
191impl Serializable for NodeValue {
192    fn write_into<W: ByteWriter>(&self, target: &mut W) {
193        match self {
194            NodeValue::EmptySubtreeRoot => target.write_u64(u64::MAX),
195            NodeValue::Present(w) => w.write_into(target),
196        }
197    }
198}
199
200impl Deserializable for NodeValue {
201    fn min_serialized_size() -> usize {
202        size_of::<u64>()
203    }
204
205    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
206        let first_value = source.read_u64()?;
207
208        let to_e = |e: FeltFromIntError| DeserializationError::InvalidValue(e.to_string());
209
210        if first_value == u64::MAX {
211            Ok(Self::EmptySubtreeRoot)
212        } else {
213            // We start by reading the rest of the bytes here to make sure that we have enough data
214            // before actually deserializing.
215            let remaining_values: [u64; Word::NUM_ELEMENTS - 1] = source.read()?;
216
217            let felts = [
218                Felt::new(first_value).map_err(to_e)?,
219                Felt::new(remaining_values[0]).map_err(to_e)?,
220                Felt::new(remaining_values[1]).map_err(to_e)?,
221                Felt::new(remaining_values[2]).map_err(to_e)?,
222            ];
223
224            Ok(Self::Present(Word::new(felts)))
225        }
226    }
227}