miden_crypto/merkle/smt/partial/serialization/
mod.rs1pub 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#[derive(Clone, Debug, Eq, PartialEq)]
39#[cfg_attr(
40 all(feature = "arbitrary", test),
41 miden_test_serialization_macros::serialization_test
42)]
43pub struct UniqueNodes {
44 pub root: Word,
49
50 pub nodes: BTreeMap<NodeIndex, Word>,
54
55 pub leaves: BTreeMap<u64, SmtLeaf>,
59
60 pub value_only_leaves: BTreeMap<u64, Word>,
64}
65
66impl UniqueNodes {
67 pub fn empty() -> Self {
69 Self {
70 root: *EmptySubtreeRoots::entry(SMT_DEPTH, 0),
71 nodes: BTreeMap::new(),
72 leaves: BTreeMap::new(),
73 value_only_leaves: BTreeMap::new(),
74 }
75 }
76
77 pub fn get_leaf_hash(&self, position: u64) -> Word {
79 self.leaves
80 .get(&position)
81 .map(SmtLeaf::hash)
82 .or_else(|| self.value_only_leaves.get(&position).copied())
83 .unwrap_or_else(|| SmtLeaf::new_empty(LeafIndex::new_max_depth(position)).hash())
84 }
85
86 pub fn get_node_hash(&self, index: NodeIndex) -> Word {
88 self.nodes
89 .get(&index)
90 .copied()
91 .unwrap_or_else(|| *EmptySubtreeRoots::entry(SMT_DEPTH, index.depth()))
92 }
93
94 pub(super) fn validate(&self) -> Result<(), DeserializationError> {
96 for (&position, leaf) in &self.leaves {
97 if self.value_only_leaves.contains_key(&position) {
98 return Err(DeserializationError::InvalidValue(format!(
99 "Leaf position {position} appears in both leaf maps"
100 )));
101 }
102 if position != leaf.index().position() {
103 return Err(DeserializationError::InvalidValue(format!(
104 "Node index {position} did not match the embedded leaf index {}",
105 leaf.index().position()
106 )));
107 }
108 }
109
110 Ok(())
111 }
112}
113
114impl Default for UniqueNodes {
115 fn default() -> Self {
116 Self::empty()
117 }
118}
119
120impl Serializable for UniqueNodes {
121 fn write_into<W: ByteWriter>(&self, target: &mut W) {
122 self.validate().expect("cannot serialize invalid UniqueNodes");
123
124 self.root.write_into(target);
126
127 let mut levels = self.nodes.iter().peekable();
130 let level_count = self
131 .nodes
132 .keys()
133 .map(NodeIndex::depth)
134 .fold((0, None), |(count, previous), depth| {
135 (count + u64::from(previous != Some(depth)), Some(depth))
136 })
137 .0;
138 target.write(level_count);
139
140 while let Some((index, _)) = levels.peek() {
141 let depth = index.depth();
142 target.write(depth);
143 let level_node_count =
144 levels.clone().take_while(|(index, _)| index.depth() == depth).count();
145 target.write(level_node_count as u64);
146 for (index, value) in levels.by_ref().take(level_node_count) {
147 target.write(index.position());
148 target.write(value);
149 }
150 }
151
152 let leaf_count = self.leaves.len() as u64;
154 target.write(leaf_count);
155 target.write_many(self.leaves.iter());
156
157 let value_only_leaf_count = self.value_only_leaves.len() as u64;
159 target.write(value_only_leaf_count);
160 target.write_many(self.value_only_leaves.iter());
161 }
162}
163
164impl Deserializable for UniqueNodes {
165 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
166 let root = Word::read_from(source)?;
169
170 let level_count = source.read_u64()?;
172 let mut nodes = BTreeMap::new();
173 let mut previous_depth = None;
174
175 for _ in 0..level_count {
177 let depth = source.read_u8()?;
178 if let Some(previous) = previous_depth
179 && depth <= previous
180 {
181 return Err(DeserializationError::InvalidValue(format!(
182 "Level depth {depth} does not exceed previous depth {previous}"
183 )));
184 }
185 previous_depth = Some(depth);
186 let node_count = source.read_u64()?;
187 if node_count == 0 {
188 return Err(DeserializationError::InvalidValue(format!(
189 "Empty node level at depth {depth}"
190 )));
191 }
192 let mut previous_position = None;
193 for _ in 0..node_count {
194 let position = source.read_u64()?;
195 if let Some(previous) = previous_position
196 && position < previous
197 {
198 return Err(DeserializationError::InvalidValue(format!(
199 "Node position {position} precedes {previous} at depth {depth}"
200 )));
201 }
202 previous_position = Some(position);
203 let index = NodeIndex::new(depth, position)
204 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
205 let value = source.read()?;
206 if nodes.insert(index, value).is_some() {
207 return Err(DeserializationError::InvalidValue(format!(
208 "Duplicate node position {position} at depth {depth}"
209 )));
210 }
211 }
212 }
213
214 let leaf_count = source.read_u64()?;
216 let mut leaves = BTreeMap::new();
217 let mut previous_position = None;
218
219 for _ in 0..leaf_count {
221 let (position, leaf) = source.read()?;
222 if let Some(previous) = previous_position
223 && position < previous
224 {
225 return Err(DeserializationError::InvalidValue(format!(
226 "Leaf position {position} precedes {previous}"
227 )));
228 }
229 previous_position = Some(position);
230 if leaves.insert(position, leaf).is_some() {
231 return Err(DeserializationError::InvalidValue(format!(
232 "Duplicate leaf position {position}"
233 )));
234 }
235 }
236
237 let value_only_leaf_count = source.read_u64()?;
239 let mut value_only_leaves = BTreeMap::new();
240 let mut previous_position = None;
241
242 for _ in 0..value_only_leaf_count {
244 let (position, value) = source.read()?;
245 if let Some(previous) = previous_position
246 && position < previous
247 {
248 return Err(DeserializationError::InvalidValue(format!(
249 "Value-only leaf position {position} precedes {previous}"
250 )));
251 }
252 previous_position = Some(position);
253 if value_only_leaves.insert(position, value).is_some() {
254 return Err(DeserializationError::InvalidValue(format!(
255 "Duplicate value-only leaf position {position}"
256 )));
257 }
258 }
259
260 let unique_nodes = Self { root, nodes, leaves, value_only_leaves };
261 unique_nodes.validate()?;
262 Ok(unique_nodes)
263 }
264
265 fn min_serialized_size() -> usize {
266 32 + 3 * 8 }
268}
269
270#[cfg(any(test, feature = "arbitrary"))]
274mod unique_nodes_arbitrary {
275 use alloc::collections::BTreeMap;
276
277 use proptest::prelude::*;
278
279 use super::UniqueNodes;
280 use crate::{
281 Felt, Word,
282 merkle::{NodeIndex, smt::Smt},
283 };
284
285 impl Arbitrary for UniqueNodes {
286 type Parameters = ();
287 type Strategy = BoxedStrategy<Self>;
288
289 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
290 (
294 any::<Word>(),
295 proptest::collection::vec((0u8..=64, 0usize..=3), 0..=4),
296 proptest::collection::vec((any::<Word>(), any::<Word>()), 0..=3),
297 proptest::collection::vec(any::<u64>(), 0..=3),
298 )
299 .prop_map(|(root, levels, leaf_pairs, value_only_positions)| {
300 let mut nodes = BTreeMap::new();
301 for (depth, count) in levels {
302 let mask = if depth == 64 { u64::MAX } else { (1u64 << depth) - 1 };
303 for i in 0..count as u64 {
304 let position = i.wrapping_mul(0x9e37_79b9_7f4a_7c15) & mask;
305 let hash: Word = [
309 Felt::from(i as u32 + 1),
310 Felt::from(depth as u32),
311 Felt::from(count as u32),
312 Felt::from(position as u32 % 7),
313 ]
314 .into();
315 nodes.insert(
316 NodeIndex::new(depth, position).expect("position fits the depth"),
317 hash,
318 );
319 }
320 }
321
322 let entries: BTreeMap<Word, Word> = leaf_pairs.into_iter().collect();
326 let smt = Smt::with_entries(entries).expect("keys are distinct");
327 let leaves: BTreeMap<u64, crate::merkle::smt::SmtLeaf> = smt
328 .leaves()
329 .map(|(index, leaf)| (index.position(), leaf.clone()))
330 .collect();
331
332 let value_only_leaves: BTreeMap<u64, Word> = value_only_positions
333 .into_iter()
334 .filter(|position| !leaves.contains_key(position))
335 .map(|p| {
336 let hash: Word = [
337 Felt::from(p as u32),
338 Felt::from((p as u32).wrapping_add(1)),
339 Felt::from((p as u32).wrapping_add(2)),
340 Felt::from((p as u32).wrapping_add(3)),
341 ]
342 .into();
343 (p, hash)
344 })
345 .collect();
346 Self { root, nodes, leaves, value_only_leaves }
347 })
348 .boxed()
349 }
350 }
351}