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::{collections::BTreeMap, string::ToString};
8
9use miden_field::Word;
10use miden_serde_utils::{
11    ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
12};
13
14use crate::merkle::{
15    EmptySubtreeRoots, NodeIndex,
16    smt::{LeafIndex, SMT_DEPTH, SmtLeaf},
17};
18
19// UNIQUE NODES
20// ================================================================================================
21
22/// A representation of a partial SMT that contains only the unique nodes in the tree, designed for
23/// better efficiency when sending data across the wire.
24///
25/// It _explicitly_ does not need to contain a fully-realized SMT, and instead may contain some
26/// subset of a full tree. It contains the minimal set of data necessary to reconstruct its input.
27///
28/// # Versioning
29///
30/// Note that this structure is explicitly **not intended to be versioned**. This structure should
31/// be used as part of a broader serialization solution that does include this if necessary.
32///
33/// # Serialization
34///
35/// Deserialization validates node indices and checks each leaf map key against the index embedded
36/// in its value.
37#[derive(Clone, Debug, Eq, PartialEq)]
38pub struct UniqueNodes {
39    /// The expected root of the tree after reconstruction.
40    ///
41    /// This primarily exists as a sanity check, taking little extra space but ensuring that we can
42    /// detect more possible cases of corruption.
43    pub root: Word,
44
45    /// The nodes that make up the tree itself.
46    ///
47    /// It maps each node index to its hash. Empty subtree roots are represented by absence.
48    pub nodes: BTreeMap<NodeIndex, Word>,
49
50    /// The leaves of the tree.
51    ///
52    /// It only stores the populated leaves, keyed on their index.
53    pub leaves: BTreeMap<u64, SmtLeaf>,
54
55    /// The leaves for which we only have the hash value, and not the actual leaf value.
56    ///
57    /// We keep these separately to the `leaves` as storing them this way is more compact.
58    pub value_only_leaves: BTreeMap<u64, Word>,
59}
60
61impl UniqueNodes {
62    /// Creates an empty `UniqueNodes` with no nodes or leaves in it.
63    pub fn empty() -> Self {
64        Self {
65            root: *EmptySubtreeRoots::entry(SMT_DEPTH, 0),
66            nodes: BTreeMap::new(),
67            leaves: BTreeMap::new(),
68            value_only_leaves: BTreeMap::new(),
69        }
70    }
71
72    /// Returns the hash of the leaf at `position`, or its canonical empty hash when absent.
73    pub fn get_leaf_hash(&self, position: u64) -> Word {
74        self.leaves
75            .get(&position)
76            .map(SmtLeaf::hash)
77            .or_else(|| self.value_only_leaves.get(&position).copied())
78            .unwrap_or_else(|| SmtLeaf::new_empty(LeafIndex::new_max_depth(position)).hash())
79    }
80
81    /// Returns the hash of the node at `index`, or its canonical empty root when absent.
82    pub fn get_node_hash(&self, index: NodeIndex) -> Word {
83        self.nodes
84            .get(&index)
85            .copied()
86            .unwrap_or_else(|| *EmptySubtreeRoots::entry(SMT_DEPTH, index.depth()))
87    }
88
89    /// Checks that each leaf is stored under its embedded tree position.
90    pub(super) fn validate(&self) -> Result<(), DeserializationError> {
91        for (&position, leaf) in &self.leaves {
92            if position != leaf.index().position() {
93                return Err(DeserializationError::InvalidValue(format!(
94                    "Node index {position} did not match the embedded leaf index {}",
95                    leaf.index().position()
96                )));
97            }
98        }
99
100        Ok(())
101    }
102}
103
104impl Default for UniqueNodes {
105    fn default() -> Self {
106        Self::empty()
107    }
108}
109
110impl Serializable for UniqueNodes {
111    fn write_into<W: ByteWriter>(&self, target: &mut W) {
112        // First we write the expected root into the buffer.
113        self.root.write_into(target);
114
115        // `NodeIndex` sorts first by depth and then by position. Since `nodes` is a `BTreeMap`, all
116        // nodes at the same depth are next to each other and can be written as one level.
117        let mut levels = self.nodes.iter().peekable();
118        let level_count = self
119            .nodes
120            .keys()
121            .map(NodeIndex::depth)
122            .fold((0, None), |(count, previous), depth| {
123                (count + u64::from(previous != Some(depth)), Some(depth))
124            })
125            .0;
126        target.write(level_count);
127
128        while let Some((index, _)) = levels.peek() {
129            let depth = index.depth();
130            target.write(depth);
131            let level_node_count =
132                levels.clone().take_while(|(index, _)| index.depth() == depth).count();
133            target.write(level_node_count as u64);
134            for (index, value) in levels.by_ref().take(level_node_count) {
135                target.write(index.position());
136                target.write(value);
137            }
138        }
139
140        // The leaves themselves come next.
141        let leaf_count = self.leaves.len() as u64;
142        target.write(leaf_count);
143        target.write_many(self.leaves.iter());
144
145        // And the value-only leaves bring up the rear.
146        let value_only_leaf_count = self.value_only_leaves.len() as u64;
147        target.write(value_only_leaf_count);
148        target.write_many(self.value_only_leaves.iter());
149    }
150}
151
152impl Deserializable for UniqueNodes {
153    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
154        // The first item is the 32 bytes containing the expected root of the tree after
155        // reconstruction.
156        let root = Word::read_from(source)?;
157
158        // We first have to read the count of levels.
159        let level_count = source.read_u64()?;
160        let mut nodes = BTreeMap::new();
161
162        // Next we have that many levels to read, but each is of a variable size.
163        for _ in 0..level_count {
164            let depth = source.read_u8()?;
165            let node_count = source.read_u64()?;
166            for _ in 0..node_count {
167                let position = source.read_u64()?;
168                let index = NodeIndex::new(depth, position)
169                    .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
170                let value = source.read()?;
171                nodes.insert(index, value);
172            }
173        }
174
175        // Next we need the number of leaves.
176        let leaf_count = source.read_u64()?;
177        let mut leaves = BTreeMap::new();
178
179        // And then we have to read that many leaves.
180        for _ in 0..leaf_count {
181            let (position, leaf) = source.read()?;
182            leaves.insert(position, leaf);
183        }
184
185        // Finally we read the number of value-only leaves...
186        let value_only_leaf_count = source.read_u64()?;
187        let mut value_only_leaves = BTreeMap::new();
188
189        // ... and read that many.
190        for _ in 0..value_only_leaf_count {
191            let (position, value) = source.read()?;
192            value_only_leaves.insert(position, value);
193        }
194
195        let unique_nodes = Self { root, nodes, leaves, value_only_leaves };
196        unique_nodes.validate()?;
197        Ok(unique_nodes)
198    }
199}