miden_core/mast/mod.rs
1use alloc::{
2 collections::{BTreeMap, BTreeSet},
3 sync::Arc,
4 vec::Vec,
5};
6use core::{
7 fmt,
8 ops::{Index, IndexMut},
9};
10
11pub use miden_utils_indexing::{IndexVec, IndexedVecError};
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15mod node;
16#[cfg(any(test, feature = "arbitrary"))]
17pub use node::arbitrary;
18pub use node::{
19 BasicBlockNode, BasicBlockNodeBuilder, CallNode, CallNodeBuilder, DecoratedOpLink,
20 DecoratorOpLinkIterator, DecoratorStore, DynNode, DynNodeBuilder, ExternalNode,
21 ExternalNodeBuilder, JoinNode, JoinNodeBuilder, LoopNode, LoopNodeBuilder,
22 MastForestContributor, MastNode, MastNodeBuilder, MastNodeErrorContext, MastNodeExt,
23 OP_BATCH_SIZE, OP_GROUP_SIZE, OpBatch, OperationOrDecorator, SplitNode, SplitNodeBuilder,
24};
25
26use crate::{
27 AdviceMap, Decorator, Felt, Idx, LexicographicWord, Word,
28 crypto::hash::Hasher,
29 utils::{ByteWriter, DeserializationError, Serializable, hash_string_to_word},
30};
31
32mod debuginfo;
33pub use debuginfo::{
34 DebugInfo, DecoratedLinks, DecoratedLinksIter, DecoratorIndexError, NodeToDecoratorIds,
35 OpToDecoratorIds,
36};
37
38mod serialization;
39
40mod merger;
41pub(crate) use merger::MastForestMerger;
42pub use merger::MastForestRootMap;
43
44mod multi_forest_node_iterator;
45pub(crate) use multi_forest_node_iterator::*;
46
47mod node_fingerprint;
48pub use node_fingerprint::{DecoratorFingerprint, MastNodeFingerprint};
49
50#[cfg(test)]
51mod tests;
52
53// MAST FOREST
54// ================================================================================================
55
56/// Represents one or more procedures, represented as a collection of [`MastNode`]s.
57///
58/// A [`MastForest`] does not have an entrypoint, and hence is not executable. A [`crate::Program`]
59/// can be built from a [`MastForest`] to specify an entrypoint.
60#[derive(Clone, Debug, Default, PartialEq, Eq)]
61pub struct MastForest {
62 /// All of the nodes local to the trees comprising the MAST forest.
63 nodes: IndexVec<MastNodeId, MastNode>,
64
65 /// Roots of procedures defined within this MAST forest.
66 roots: Vec<MastNodeId>,
67
68 /// Advice map to be loaded into the VM prior to executing procedures from this MAST forest.
69 advice_map: AdviceMap,
70
71 /// Debug information including decorators and error codes.
72 /// Always present (as per issue #1821), but can be empty for stripped builds.
73 debug_info: DebugInfo,
74}
75
76// ------------------------------------------------------------------------------------------------
77/// Constructors
78impl MastForest {
79 /// Creates a new empty [`MastForest`].
80 pub fn new() -> Self {
81 Self {
82 nodes: IndexVec::new(),
83 roots: Vec::new(),
84 advice_map: AdviceMap::default(),
85 debug_info: DebugInfo::new(),
86 }
87 }
88}
89
90// ------------------------------------------------------------------------------------------------
91/// State mutators
92impl MastForest {
93 /// The maximum number of nodes that can be stored in a single MAST forest.
94 const MAX_NODES: usize = (1 << 30) - 1;
95
96 /// Marks the given [`MastNodeId`] as being the root of a procedure.
97 ///
98 /// If the specified node is already marked as a root, this will have no effect.
99 ///
100 /// # Panics
101 /// - if `new_root_id`'s internal index is larger than the number of nodes in this forest (i.e.
102 /// clearly doesn't belong to this MAST forest).
103 pub fn make_root(&mut self, new_root_id: MastNodeId) {
104 assert!(new_root_id.to_usize() < self.nodes.len());
105
106 if !self.roots.contains(&new_root_id) {
107 self.roots.push(new_root_id);
108 }
109 }
110
111 /// Removes all nodes in the provided set from the MAST forest. The nodes MUST be orphaned (i.e.
112 /// have no parent). Otherwise, this parent's reference is considered "dangling" after the
113 /// removal (i.e. will point to an incorrect node after the removal), and this removal operation
114 /// would result in an invalid [`MastForest`].
115 ///
116 /// It also returns the map from old node IDs to new node IDs. Any [`MastNodeId`] used in
117 /// reference to the old [`MastForest`] should be remapped using this map.
118 pub fn remove_nodes(
119 &mut self,
120 nodes_to_remove: &BTreeSet<MastNodeId>,
121 ) -> BTreeMap<MastNodeId, MastNodeId> {
122 if nodes_to_remove.is_empty() {
123 return BTreeMap::new();
124 }
125
126 let old_nodes = core::mem::replace(&mut self.nodes, IndexVec::new());
127 let old_root_ids = core::mem::take(&mut self.roots);
128 let (retained_nodes, id_remappings) = remove_nodes(old_nodes.into_inner(), nodes_to_remove);
129
130 self.remap_and_add_nodes(retained_nodes, &id_remappings);
131 self.remap_and_add_roots(old_root_ids, &id_remappings);
132 id_remappings
133 }
134
135 /// Removes all decorators from this MAST forest.
136 ///
137 /// This method modifies the forest in-place, removing all decorator information
138 /// including operation-indexed decorators, before-enter decorators, after-exit
139 /// decorators, and error codes.
140 ///
141 /// # Examples
142 ///
143 /// ```rust
144 /// use miden_core::mast::MastForest;
145 ///
146 /// let mut forest = MastForest::new();
147 /// // Add decorators and nodes to the forest
148 /// forest.strip_decorators(); // forest is now stripped
149 /// ```
150 pub fn strip_decorators(&mut self) {
151 // Clear all debug info (decorators and error codes)
152 self.debug_info.clear();
153 }
154
155 /// Compacts the forest by merging duplicate nodes.
156 ///
157 /// This operation performs node deduplication by merging the forest with itself.
158 /// The method assumes that decorators have already been stripped if that is desired.
159 /// The operation modifies the forest in-place, updating all node references as needed.
160 ///
161 /// The process works by:
162 /// 1. Merging the forest with itself to deduplicate identical nodes
163 /// 2. Updating internal node references and remappings
164 /// 3. Modifying the forest in-place with the compacted result
165 ///
166 /// # Examples
167 ///
168 /// ```rust
169 /// use miden_core::mast::MastForest;
170 ///
171 /// let mut forest = MastForest::new();
172 /// // Add nodes to the forest
173 ///
174 /// // First strip decorators if needed
175 /// forest.strip_decorators();
176 ///
177 /// // Then compact the forest
178 /// let root_map = forest.compact();
179 ///
180 /// // Forest is now compacted with duplicate nodes merged
181 /// ```
182 pub fn compact(&mut self) -> MastForestRootMap {
183 // Merge with itself to deduplicate nodes
184 // Note: This cannot fail for a self-merge under normal conditions.
185 // The only possible failures (TooManyNodes, TooManyDecorators) would require the
186 // original forest to be at capacity limits, at which point compaction wouldn't help.
187 let (compacted_forest, root_map) = MastForest::merge([&*self])
188 .expect("Failed to compact MastForest: this should never happen during self-merge");
189
190 // Replace current forest with compacted version
191 *self = compacted_forest;
192
193 root_map
194 }
195
196 /// Merges all `forests` into a new [`MastForest`].
197 ///
198 /// Merging two forests means combining all their constituent parts, i.e. [`MastNode`]s,
199 /// [`Decorator`]s and roots. During this process, any duplicate or
200 /// unreachable nodes are removed. Additionally, [`MastNodeId`]s of nodes as well as
201 /// [`DecoratorId`]s of decorators may change and references to them are remapped to their new
202 /// location.
203 ///
204 /// For example, consider this representation of a forest's nodes with all of these nodes being
205 /// roots:
206 ///
207 /// ```text
208 /// [Block(foo), Block(bar)]
209 /// ```
210 ///
211 /// If we merge another forest into it:
212 ///
213 /// ```text
214 /// [Block(bar), Call(0)]
215 /// ```
216 ///
217 /// then we would expect this forest:
218 ///
219 /// ```text
220 /// [Block(foo), Block(bar), Call(1)]
221 /// ```
222 ///
223 /// - The `Call` to the `bar` block was remapped to its new index (now 1, previously 0).
224 /// - The `Block(bar)` was deduplicated any only exists once in the merged forest.
225 ///
226 /// The function also returns a vector of [`MastForestRootMap`]s, whose length equals the number
227 /// of passed `forests`. The indices in the vector correspond to the ones in `forests`. The map
228 /// of a given forest contains the new locations of its roots in the merged forest. To
229 /// illustrate, the above example would return a vector of two maps:
230 ///
231 /// ```text
232 /// vec![{0 -> 0, 1 -> 1}
233 /// {0 -> 1, 1 -> 2}]
234 /// ```
235 ///
236 /// - The root locations of the original forest are unchanged.
237 /// - For the second forest, the `bar` block has moved from index 0 to index 1 in the merged
238 /// forest, and the `Call` has moved from index 1 to 2.
239 ///
240 /// If any forest being merged contains an `External(qux)` node and another forest contains a
241 /// node whose digest is `qux`, then the external node will be replaced with the `qux` node,
242 /// which is effectively deduplication. Decorators are ignored when it comes to merging
243 /// External nodes. This means that an External node with decorators may be replaced by a node
244 /// without decorators or vice versa.
245 pub fn merge<'forest>(
246 forests: impl IntoIterator<Item = &'forest MastForest>,
247 ) -> Result<(MastForest, MastForestRootMap), MastForestError> {
248 MastForestMerger::merge(forests)
249 }
250}
251
252// ------------------------------------------------------------------------------------------------
253/// Helpers
254impl MastForest {
255 /// Adds all provided nodes to the internal set of nodes, remapping all [`MastNodeId`]
256 /// references in those nodes.
257 ///
258 /// # Panics
259 /// - Panics if the internal set of nodes is not empty.
260 fn remap_and_add_nodes(
261 &mut self,
262 nodes_to_add: Vec<MastNode>,
263 id_remappings: &BTreeMap<MastNodeId, MastNodeId>,
264 ) {
265 assert!(self.nodes.is_empty());
266 // extract decorator information from the nodes by converting them into builders
267 let node_builders =
268 nodes_to_add.into_iter().map(|node| node.to_builder(self)).collect::<Vec<_>>();
269
270 // Clear decorator storage after extracting builders (builders contain decorator data)
271 self.debug_info.clear_mappings();
272
273 // Add each node to the new MAST forest, making sure to rewrite any outdated internal
274 // `MastNodeId`s
275 for live_node_builder in node_builders {
276 live_node_builder.remap_children(id_remappings).add_to_forest(self).unwrap();
277 }
278 }
279
280 /// Remaps and adds all old root ids to the internal set of roots.
281 ///
282 /// # Panics
283 /// - Panics if the internal set of roots is not empty.
284 fn remap_and_add_roots(
285 &mut self,
286 old_root_ids: Vec<MastNodeId>,
287 id_remappings: &BTreeMap<MastNodeId, MastNodeId>,
288 ) {
289 assert!(self.roots.is_empty());
290
291 for old_root_id in old_root_ids {
292 let new_root_id = id_remappings.get(&old_root_id).copied().unwrap_or(old_root_id);
293 self.make_root(new_root_id);
294 }
295 }
296}
297
298/// Returns the set of nodes that are live, as well as the mapping from "old ID" to "new ID" for all
299/// live nodes.
300fn remove_nodes(
301 mast_nodes: Vec<MastNode>,
302 nodes_to_remove: &BTreeSet<MastNodeId>,
303) -> (Vec<MastNode>, BTreeMap<MastNodeId, MastNodeId>) {
304 // Note: this allows us to safely use `usize as u32`, guaranteeing that it won't wrap around.
305 assert!(mast_nodes.len() < u32::MAX as usize);
306
307 let mut retained_nodes = Vec::with_capacity(mast_nodes.len());
308 let mut id_remappings = BTreeMap::new();
309
310 for (old_node_index, old_node) in mast_nodes.into_iter().enumerate() {
311 let old_node_id: MastNodeId = MastNodeId(old_node_index as u32);
312
313 if !nodes_to_remove.contains(&old_node_id) {
314 let new_node_id: MastNodeId = MastNodeId(retained_nodes.len() as u32);
315 id_remappings.insert(old_node_id, new_node_id);
316
317 retained_nodes.push(old_node);
318 }
319 }
320
321 (retained_nodes, id_remappings)
322}
323
324// ------------------------------------------------------------------------------------------------
325/// Public accessors
326impl MastForest {
327 /// Returns the [`MastNode`] associated with the provided [`MastNodeId`] if valid, or else
328 /// `None`.
329 ///
330 /// This is the fallible version of indexing (e.g. `mast_forest[node_id]`).
331 #[inline(always)]
332 pub fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
333 self.nodes.get(node_id)
334 }
335
336 /// Returns the [`MastNodeId`] of the procedure associated with a given digest, if any.
337 #[inline(always)]
338 pub fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
339 self.roots.iter().find(|&&root_id| self[root_id].digest() == digest).copied()
340 }
341
342 /// Returns true if a node with the specified ID is a root of a procedure in this MAST forest.
343 pub fn is_procedure_root(&self, node_id: MastNodeId) -> bool {
344 self.roots.contains(&node_id)
345 }
346
347 /// Returns an iterator over the digests of all procedures in this MAST forest.
348 pub fn procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
349 self.roots.iter().map(|&root_id| self[root_id].digest())
350 }
351
352 /// Returns an iterator over the digests of local procedures in this MAST forest.
353 ///
354 /// A local procedure is defined as a procedure which is not a single external node.
355 pub fn local_procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
356 self.roots.iter().filter_map(|&root_id| {
357 let node = &self[root_id];
358 if node.is_external() { None } else { Some(node.digest()) }
359 })
360 }
361
362 /// Returns an iterator over the IDs of the procedures in this MAST forest.
363 pub fn procedure_roots(&self) -> &[MastNodeId] {
364 &self.roots
365 }
366
367 /// Returns the number of procedures in this MAST forest.
368 pub fn num_procedures(&self) -> u32 {
369 self.roots
370 .len()
371 .try_into()
372 .expect("MAST forest contains more than 2^32 procedures.")
373 }
374
375 /// Returns the [Word] representing the content hash of a subset of [`MastNodeId`]s.
376 ///
377 /// # Panics
378 /// This function panics if any `node_ids` is not a node of this forest.
379 pub fn compute_nodes_commitment<'a>(
380 &self,
381 node_ids: impl IntoIterator<Item = &'a MastNodeId>,
382 ) -> Word {
383 let mut digests: Vec<Word> = node_ids.into_iter().map(|&id| self[id].digest()).collect();
384 digests.sort_unstable_by_key(|word| LexicographicWord::from(*word));
385 miden_crypto::hash::rpo::Rpo256::merge_many(&digests)
386 }
387
388 /// Returns the number of nodes in this MAST forest.
389 pub fn num_nodes(&self) -> u32 {
390 self.nodes.len() as u32
391 }
392
393 /// Returns the underlying nodes in this MAST forest.
394 pub fn nodes(&self) -> &[MastNode] {
395 self.nodes.as_slice()
396 }
397
398 pub fn advice_map(&self) -> &AdviceMap {
399 &self.advice_map
400 }
401
402 pub fn advice_map_mut(&mut self) -> &mut AdviceMap {
403 &mut self.advice_map
404 }
405}
406
407// ------------------------------------------------------------------------------------------------
408/// Decorator methods
409impl MastForest {
410 /// Returns a list of all decorators contained in this [MastForest].
411 pub fn decorators(&self) -> &[Decorator] {
412 self.debug_info.decorators()
413 }
414
415 /// Returns the [`Decorator`] associated with the provided [`DecoratorId`] if valid, or else
416 /// `None`.
417 ///
418 /// This is the fallible version of indexing (e.g. `mast_forest[decorator_id]`).
419 #[inline]
420 pub fn decorator_by_id(&self, decorator_id: DecoratorId) -> Option<&Decorator> {
421 self.debug_info.decorator(decorator_id)
422 }
423
424 /// Returns decorator indices for a specific operation within a node.
425 ///
426 /// This is the primary accessor for reading decorators from the centralized storage.
427 /// Returns a slice of decorator IDs for the given operation.
428 #[inline]
429 pub(crate) fn decorator_indices_for_op(
430 &self,
431 node_id: MastNodeId,
432 local_op_idx: usize,
433 ) -> &[DecoratorId] {
434 self.debug_info.decorators_for_operation(node_id, local_op_idx)
435 }
436
437 /// Returns an iterator over decorator references for a specific operation within a node.
438 ///
439 /// This is the preferred method for accessing decorators, as it provides direct
440 /// references to the decorator objects.
441 #[inline]
442 pub fn decorators_for_op<'a>(
443 &'a self,
444 node_id: MastNodeId,
445 local_op_idx: usize,
446 ) -> impl Iterator<Item = &'a Decorator> + 'a {
447 self.decorator_indices_for_op(node_id, local_op_idx)
448 .iter()
449 .map(move |&decorator_id| &self[decorator_id])
450 }
451
452 /// Returns the decorators to be executed before this node is executed.
453 #[inline]
454 pub fn before_enter_decorators(&self, node_id: MastNodeId) -> &[DecoratorId] {
455 self.debug_info.before_enter_decorators(node_id)
456 }
457
458 /// Returns the decorators to be executed after this node is executed.
459 #[inline]
460 pub fn after_exit_decorators(&self, node_id: MastNodeId) -> &[DecoratorId] {
461 self.debug_info.after_exit_decorators(node_id)
462 }
463
464 /// Returns decorator links for a node, including operation indices.
465 ///
466 /// This provides a flattened view of all decorators for a node with their operation indices.
467 #[inline]
468 pub(crate) fn decorator_links_for_node<'a>(
469 &'a self,
470 node_id: MastNodeId,
471 ) -> Result<DecoratedLinks<'a>, DecoratorIndexError> {
472 self.debug_info.decorator_links_for_node(node_id)
473 }
474
475 /// Adds a decorator to the forest, and returns the associated [`DecoratorId`].
476 pub fn add_decorator(&mut self, decorator: Decorator) -> Result<DecoratorId, MastForestError> {
477 self.debug_info.add_decorator(decorator)
478 }
479
480 /// Adds decorator IDs for a node to the storage.
481 ///
482 /// Used when building nodes for efficient decorator access during execution.
483 ///
484 /// # Note
485 /// This method does not validate decorator IDs immediately. Validation occurs during
486 /// operations that need to access the actual decorator data (e.g., merging, serialization).
487 #[inline]
488 pub(crate) fn register_node_decorators(
489 &mut self,
490 node_id: MastNodeId,
491 before_enter: &[DecoratorId],
492 after_exit: &[DecoratorId],
493 ) {
494 self.debug_info.register_node_decorators(node_id, before_enter, after_exit);
495 }
496}
497
498// ------------------------------------------------------------------------------------------------
499/// Error message methods
500impl MastForest {
501 /// Given an error code as a Felt, resolves it to its corresponding error message.
502 pub fn resolve_error_message(&self, code: Felt) -> Option<Arc<str>> {
503 let key = u64::from(code);
504 self.debug_info.error_message(key)
505 }
506
507 /// Registers an error message in the MAST Forest and returns the corresponding error code as a
508 /// Felt.
509 pub fn register_error(&mut self, msg: Arc<str>) -> Felt {
510 let code: Felt = error_code_from_msg(&msg);
511 // we use u64 as keys for the map
512 self.debug_info.insert_error_code(code.as_int(), msg);
513 code
514 }
515}
516
517// MAST FOREST INDEXING
518// ------------------------------------------------------------------------------------------------
519
520impl Index<MastNodeId> for MastForest {
521 type Output = MastNode;
522
523 #[inline(always)]
524 fn index(&self, node_id: MastNodeId) -> &Self::Output {
525 &self.nodes[node_id]
526 }
527}
528
529impl IndexMut<MastNodeId> for MastForest {
530 #[inline(always)]
531 fn index_mut(&mut self, node_id: MastNodeId) -> &mut Self::Output {
532 &mut self.nodes[node_id]
533 }
534}
535
536impl Index<DecoratorId> for MastForest {
537 type Output = Decorator;
538
539 #[inline(always)]
540 fn index(&self, decorator_id: DecoratorId) -> &Self::Output {
541 self.debug_info.decorator(decorator_id).expect("DecoratorId out of bounds")
542 }
543}
544
545impl IndexMut<DecoratorId> for MastForest {
546 #[inline(always)]
547 fn index_mut(&mut self, decorator_id: DecoratorId) -> &mut Self::Output {
548 self.debug_info.decorator_mut(decorator_id).expect("DecoratorId out of bounds")
549 }
550}
551
552// MAST NODE ID
553// ================================================================================================
554
555/// An opaque handle to a [`MastNode`] in some [`MastForest`]. It is the responsibility of the user
556/// to use a given [`MastNodeId`] with the corresponding [`MastForest`].
557///
558/// Note that the [`MastForest`] does *not* ensure that equal [`MastNode`]s have equal
559/// [`MastNodeId`] handles. Hence, [`MastNodeId`] equality must not be used to test for equality of
560/// the underlying [`MastNode`].
561#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
562#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
563#[cfg_attr(feature = "serde", serde(transparent))]
564#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
565pub struct MastNodeId(u32);
566
567/// Operations that mutate a MAST often produce this mapping between old and new NodeIds.
568pub type Remapping = BTreeMap<MastNodeId, MastNodeId>;
569
570impl MastNodeId {
571 /// Returns a new `MastNodeId` with the provided inner value, or an error if the provided
572 /// `value` is greater than the number of nodes in the forest.
573 ///
574 /// For use in deserialization.
575 pub fn from_u32_safe(
576 value: u32,
577 mast_forest: &MastForest,
578 ) -> Result<Self, DeserializationError> {
579 Self::from_u32_with_node_count(value, mast_forest.nodes.len())
580 }
581
582 /// Returns a new [`MastNodeId`] with the provided `node_id`, or an error if `node_id` is
583 /// greater than the number of nodes in the [`MastForest`] for which this ID is being
584 /// constructed.
585 pub fn from_usize_safe(
586 node_id: usize,
587 mast_forest: &MastForest,
588 ) -> Result<Self, DeserializationError> {
589 let node_id: u32 = node_id.try_into().map_err(|_| {
590 DeserializationError::InvalidValue(format!(
591 "node id '{node_id}' does not fit into a u32"
592 ))
593 })?;
594 MastNodeId::from_u32_safe(node_id, mast_forest)
595 }
596
597 /// Returns a new [`MastNodeId`] from the given `value` without checking its validity.
598 pub(crate) fn new_unchecked(value: u32) -> Self {
599 Self(value)
600 }
601
602 /// Returns a new [`MastNodeId`] with the provided `id`, or an error if `id` is greater or equal
603 /// to `node_count`. The `node_count` is the total number of nodes in the [`MastForest`] for
604 /// which this ID is being constructed.
605 ///
606 /// This function can be used when deserializing an id whose corresponding node is not yet in
607 /// the forest and [`Self::from_u32_safe`] would fail. For instance, when deserializing the ids
608 /// referenced by the Join node in this forest:
609 ///
610 /// ```text
611 /// [Join(1, 2), Block(foo), Block(bar)]
612 /// ```
613 ///
614 /// Since it is less safe than [`Self::from_u32_safe`] and usually not needed it is not public.
615 pub(super) fn from_u32_with_node_count(
616 id: u32,
617 node_count: usize,
618 ) -> Result<Self, DeserializationError> {
619 if (id as usize) < node_count {
620 Ok(Self(id))
621 } else {
622 Err(DeserializationError::InvalidValue(format!(
623 "Invalid deserialized MAST node ID '{id}', but {node_count} is the number of nodes in the forest",
624 )))
625 }
626 }
627
628 /// Remap the NodeId to its new position using the given [`Remapping`].
629 pub fn remap(&self, remapping: &Remapping) -> Self {
630 *remapping.get(self).unwrap_or(self)
631 }
632}
633
634impl From<u32> for MastNodeId {
635 fn from(value: u32) -> Self {
636 MastNodeId::new_unchecked(value)
637 }
638}
639
640impl Idx for MastNodeId {}
641
642impl From<MastNodeId> for u32 {
643 fn from(value: MastNodeId) -> Self {
644 value.0
645 }
646}
647
648impl fmt::Display for MastNodeId {
649 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
650 write!(f, "MastNodeId({})", self.0)
651 }
652}
653
654#[cfg(any(test, feature = "arbitrary"))]
655impl proptest::prelude::Arbitrary for MastNodeId {
656 type Parameters = ();
657
658 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
659 use proptest::prelude::*;
660 any::<u32>().prop_map(MastNodeId).boxed()
661 }
662
663 type Strategy = proptest::prelude::BoxedStrategy<Self>;
664}
665
666// ITERATOR
667
668/// Iterates over all the nodes a root depends on, in pre-order. The iteration can include other
669/// roots in the same forest.
670pub struct SubtreeIterator<'a> {
671 forest: &'a MastForest,
672 discovered: Vec<MastNodeId>,
673 unvisited: Vec<MastNodeId>,
674}
675impl<'a> SubtreeIterator<'a> {
676 pub fn new(root: &MastNodeId, forest: &'a MastForest) -> Self {
677 let discovered = vec![];
678 let unvisited = vec![*root];
679 SubtreeIterator { forest, discovered, unvisited }
680 }
681}
682impl Iterator for SubtreeIterator<'_> {
683 type Item = MastNodeId;
684 fn next(&mut self) -> Option<MastNodeId> {
685 while let Some(id) = self.unvisited.pop() {
686 let node = &self.forest[id];
687 if !node.has_children() {
688 return Some(id);
689 } else {
690 self.discovered.push(id);
691 node.append_children_to(&mut self.unvisited);
692 }
693 }
694 self.discovered.pop()
695 }
696}
697
698// DECORATOR ID
699// ================================================================================================
700
701/// An opaque handle to a [`Decorator`] in some [`MastForest`]. It is the responsibility of the user
702/// to use a given [`DecoratorId`] with the corresponding [`MastForest`].
703#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
704#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
705#[cfg_attr(feature = "serde", serde(transparent))]
706pub struct DecoratorId(u32);
707
708impl DecoratorId {
709 /// Returns a new `DecoratorId` with the provided inner value, or an error if the provided
710 /// `value` is greater than the number of nodes in the forest.
711 ///
712 /// For use in deserialization.
713 pub fn from_u32_safe(
714 value: u32,
715 mast_forest: &MastForest,
716 ) -> Result<Self, DeserializationError> {
717 Self::from_u32_bounded(value, mast_forest.debug_info.num_decorators())
718 }
719
720 /// Returns a new `DecoratorId` with the provided inner value, or an error if the provided
721 /// `value` is greater than or equal to `bound`.
722 ///
723 /// For use in deserialization when the bound is known without needing the full MastForest.
724 pub fn from_u32_bounded(value: u32, bound: usize) -> Result<Self, DeserializationError> {
725 if (value as usize) < bound {
726 Ok(Self(value))
727 } else {
728 Err(DeserializationError::InvalidValue(format!(
729 "Invalid deserialized MAST decorator id '{}', but allows only {} decorators",
730 value, bound,
731 )))
732 }
733 }
734
735 /// Creates a new [`DecoratorId`] without checking its validity.
736 pub(crate) fn new_unchecked(value: u32) -> Self {
737 Self(value)
738 }
739}
740
741impl From<u32> for DecoratorId {
742 fn from(value: u32) -> Self {
743 DecoratorId::new_unchecked(value)
744 }
745}
746
747impl Idx for DecoratorId {}
748
749impl From<DecoratorId> for u32 {
750 fn from(value: DecoratorId) -> Self {
751 value.0
752 }
753}
754
755impl fmt::Display for DecoratorId {
756 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
757 write!(f, "DecoratorId({})", self.0)
758 }
759}
760
761impl Serializable for DecoratorId {
762 fn write_into<W: ByteWriter>(&self, target: &mut W) {
763 self.0.write_into(target)
764 }
765}
766
767/// Derives an error code from an error message by hashing the message and returning the 0th element
768/// of the resulting [`Word`].
769pub fn error_code_from_msg(msg: impl AsRef<str>) -> Felt {
770 // hash the message and return 0th felt of the resulting Word
771 hash_string_to_word(msg.as_ref())[0]
772}
773
774// MAST FOREST ERROR
775// ================================================================================================
776
777/// Represents the types of errors that can occur when dealing with MAST forest.
778#[derive(Debug, thiserror::Error, PartialEq)]
779pub enum MastForestError {
780 #[error("MAST forest decorator count exceeds the maximum of {} decorators", u32::MAX)]
781 TooManyDecorators,
782 #[error("MAST forest node count exceeds the maximum of {} nodes", MastForest::MAX_NODES)]
783 TooManyNodes,
784 #[error("node id {0} is greater than or equal to forest length {1}")]
785 NodeIdOverflow(MastNodeId, usize),
786 #[error("decorator id {0} is greater than or equal to decorator count {1}")]
787 DecoratorIdOverflow(DecoratorId, usize),
788 #[error("basic block cannot be created from an empty list of operations")]
789 EmptyBasicBlock,
790 #[error(
791 "decorator root of child with node id {0} is missing but is required for fingerprint computation"
792 )]
793 ChildFingerprintMissing(MastNodeId),
794 #[error("advice map key {0} already exists when merging forests")]
795 AdviceMapKeyCollisionOnMerge(Word),
796 #[error("decorator storage error: {0}")]
797 DecoratorError(DecoratorIndexError),
798 #[error("digest is required for deserialization")]
799 DigestRequiredForDeserialization,
800}
801
802// Custom serde implementations for MastForest that handle linked decorators properly
803// by delegating to the existing winter-utils serialization which already handles
804// the conversion between linked and owned decorator formats.
805#[cfg(feature = "serde")]
806impl serde::Serialize for MastForest {
807 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
808 where
809 S: serde::Serializer,
810 {
811 // Use the existing winter-utils serialization which already handles linked decorators
812 let bytes = crate::utils::Serializable::to_bytes(self);
813 serializer.serialize_bytes(&bytes)
814 }
815}
816
817#[cfg(feature = "serde")]
818impl<'de> serde::Deserialize<'de> for MastForest {
819 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
820 where
821 D: serde::Deserializer<'de>,
822 {
823 // Deserialize bytes, then use winter-utils Deserializable
824 let bytes = Vec::<u8>::deserialize(deserializer)?;
825 let mut slice_reader = winter_utils::SliceReader::new(&bytes);
826 crate::utils::Deserializable::read_from(&mut slice_reader).map_err(serde::de::Error::custom)
827 }
828}