1use alloc::{
44 collections::{BTreeMap, BTreeSet},
45 string::String,
46 sync::Arc,
47 vec::Vec,
48};
49use core::{fmt, ops::Index};
50
51#[cfg(any(test, feature = "arbitrary"))]
52use proptest::prelude::*;
53#[cfg(feature = "serde")]
54use serde::{Deserialize, Serialize};
55
56#[cfg(feature = "serde")]
57use crate::serde::{Deserializable, SliceReader};
58
59mod node;
60#[cfg(any(test, feature = "arbitrary"))]
61pub use node::arbitrary;
62pub(crate) use node::collect_immediate_placements;
63pub use node::{
64 BasicBlockNode, BasicBlockNodeBuilder, CallNode, CallNodeBuilder, DynNode, DynNodeBuilder,
65 ExternalNode, ExternalNodeBuilder, JoinNode, JoinNodeBuilder, LoopNode, LoopNodeBuilder,
66 MastForestContributor, MastNode, MastNodeBuilder, MastNodeContext, MastNodeExt, OP_BATCH_SIZE,
67 OP_GROUP_SIZE, OpBatch, SplitNode, SplitNodeBuilder,
68};
69
70use crate::{
71 Felt, Word,
72 advice::AdviceMap,
73 crypto::hash::Poseidon2,
74 serde::{ByteWriter, DeserializationError, Serializable},
75 utils::{DenseIdMap, Idx, IndexVec, hash_string_to_word},
76};
77
78mod serialization;
79pub use serialization::{
80 AdviceMapView, AdviceValueView, MastForestReadMode, MastForestReadView, MastForestView,
81 MastForestWireView, MastNodeEntry, MastNodeInfo,
82};
83
84mod dense_builder;
85pub use dense_builder::DenseMastForestBuilder;
86
87mod untrusted;
88pub use untrusted::{UntrustedMastForest, UntrustedMastForestReadOptions};
89
90mod merger;
91pub(crate) use merger::MastForestMerger;
92pub use merger::MastForestRootMap;
93
94mod multi_forest_node_iterator;
95pub(crate) use multi_forest_node_iterator::*;
96
97mod node_builder_utils;
98pub use node_builder_utils::build_node_with_remapped_ids;
99
100mod sparse;
101pub use sparse::{MastForestId, SparseMastForest, SparseMastForestBuilder, VisitKind};
102
103#[cfg(test)]
104mod tests;
105
106#[derive(Clone, Debug, Default)]
126#[cfg_attr(
127 all(feature = "arbitrary", test),
128 miden_test_serde_macros::serde_test(binary_serde(true))
129)]
130pub struct MastForest {
131 nodes: IndexVec<MastNodeId, MastNode>,
133
134 roots: Vec<MastNodeId>,
136
137 advice_map: AdviceMap,
139
140 commitment: MastForestCommitment,
142}
143
144#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
155struct MastForestCommitment {
156 commitment: Word,
158
159 interface_commitment: Word,
161
162 dependency_commitment: Word,
164
165 advice_commitment: Word,
167}
168
169pub(crate) struct MastForestParts {
171 pub nodes: IndexVec<MastNodeId, MastNode>,
172 pub roots: Vec<MastNodeId>,
173 pub advice_map: AdviceMap,
174}
175
176impl MastForest {
179 pub fn new() -> Self {
181 Self {
182 nodes: IndexVec::new(),
183 roots: Vec::new(),
184 advice_map: AdviceMap::default(),
185 commitment: empty_mast_forest_commitment(),
186 }
187 }
188
189 #[doc(hidden)]
193 pub fn from_raw_parts(
194 nodes: IndexVec<MastNodeId, MastNode>,
195 roots: Vec<MastNodeId>,
196 advice_map: AdviceMap,
197 ) -> Result<Self, MastForestError> {
198 Self::from_parts(MastForestParts { nodes, roots, advice_map })
199 }
200
201 #[doc(hidden)]
207 pub fn from_raw_parts_with_id_map(
208 nodes: IndexVec<MastNodeId, MastNode>,
209 roots: Vec<MastNodeId>,
210 advice_map: AdviceMap,
211 ) -> Result<(Self, DenseIdMap<MastNodeId, MastNodeId>), MastForestError> {
212 Self::from_parts_with_id_map(MastForestParts { nodes, roots, advice_map })
213 }
214
215 pub(crate) fn from_parts(parts: MastForestParts) -> Result<Self, MastForestError> {
217 Self::from_parts_with_id_map(parts).map(|(forest, _remapping)| forest)
218 }
219
220 pub(crate) fn from_parts_with_id_map(
221 parts: MastForestParts,
222 ) -> Result<(Self, DenseIdMap<MastNodeId, MastNodeId>), MastForestError> {
223 validate_mast_forest_parts_bounds(&parts)?;
224 let (parts, id_remapping) = canonicalize_parts(parts)?;
225
226 let forest = Self {
227 commitment: compute_mast_forest_commitment(
228 &parts.nodes,
229 &parts.roots,
230 &parts.advice_map,
231 ),
232 nodes: parts.nodes,
233 roots: parts.roots,
234 advice_map: parts.advice_map,
235 };
236
237 forest.validate_dense_node_order()?;
238 forest.validate()?;
239 forest.validate_node_hashes()?;
240 Ok((forest, id_remapping))
241 }
242
243 pub(in crate::mast) fn from_trusted_deserialization_parts(
244 parts: MastForestParts,
245 ) -> Result<Self, MastForestError> {
246 validate_mast_forest_parts_bounds(&parts)?;
247 validate_dense_node_order(&parts.nodes)?;
250 Ok(Self {
251 commitment: compute_mast_forest_commitment(
252 &parts.nodes,
253 &parts.roots,
254 &parts.advice_map,
255 ),
256 nodes: parts.nodes,
257 roots: parts.roots,
258 advice_map: parts.advice_map,
259 })
260 }
261}
262
263fn validate_mast_forest_parts_bounds(parts: &MastForestParts) -> Result<(), MastForestError> {
267 if parts.nodes.len() > MastForest::MAX_NODES {
268 return Err(MastForestError::TooManyNodes);
269 }
270
271 let node_count = parts.nodes.len();
272 for &root_id in &parts.roots {
273 if root_id.to_usize() >= node_count {
274 return Err(MastForestError::NodeIdOverflow(root_id, node_count));
275 }
276 }
277
278 Ok(())
279}
280
281fn canonicalize_parts(
287 parts: MastForestParts,
288) -> Result<(MastForestParts, DenseIdMap<MastNodeId, MastNodeId>), MastForestError> {
289 let node_count = parts.nodes.len();
290 let ordered_ids = final_dense_node_order(&parts.nodes)?;
291
292 let mut remapping = DenseIdMap::with_len(node_count);
293 for (new_index, old_id) in ordered_ids.iter().copied().enumerate() {
294 remapping.insert(old_id, MastNodeId::new_unchecked(new_index as u32));
295 }
296
297 if ordered_ids
298 .iter()
299 .enumerate()
300 .all(|(index, &old_id)| old_id == MastNodeId::new_unchecked(index as u32))
301 {
302 debug_assert!(validate_dense_node_order(&parts.nodes).is_ok());
303 return Ok((parts, remapping));
304 }
305
306 let mut nodes = IndexVec::with_capacity(node_count);
307 let empty_forest = MastForest::new();
308 for old_id in ordered_ids {
309 let node = parts.nodes[old_id].clone();
310 let remapped_node =
311 node.to_builder(&empty_forest).remap_children(&remapping).build_linked()?;
312 nodes
313 .push(remapped_node)
314 .expect("canonicalized node count was validated before remapping");
315 }
316
317 let roots = parts
318 .roots
319 .into_iter()
320 .map(|root_id| {
321 remapping
322 .get(root_id)
323 .ok_or(MastForestError::NodeIdOverflow(root_id, node_count))
324 })
325 .collect::<Result<Vec<_>, _>>()?;
326
327 debug_assert!(validate_dense_node_order(&nodes).is_ok());
328
329 Ok((
330 MastForestParts {
331 nodes,
332 roots,
333 advice_map: parts.advice_map,
334 },
335 remapping,
336 ))
337}
338
339fn final_dense_node_order(
340 nodes: &IndexVec<MastNodeId, MastNode>,
341) -> Result<Vec<MastNodeId>, MastForestError> {
342 let node_count = nodes.len();
343 let mut external_ids = Vec::new();
344 let mut basic_block_ids = Vec::new();
345 let mut internal_ids = Vec::new();
346
347 for (index, node) in nodes.iter().enumerate() {
348 let node_id = MastNodeId::new_unchecked(index as u32);
349 match node.order_class() {
350 MastNodeOrderClass::External => external_ids.push(node_id),
351 MastNodeOrderClass::BasicBlock => basic_block_ids.push(node_id),
352 MastNodeOrderClass::Internal => internal_ids.push(node_id),
353 }
354 }
355
356 external_ids.sort_by(|&left_id, &right_id| {
357 nodes[left_id]
358 .digest()
359 .cmp(&nodes[right_id].digest())
360 .then(left_id.0.cmp(&right_id.0))
361 });
362
363 let mut previous_external_digest = None;
366 for &node_id in &external_ids {
367 let digest = nodes[node_id].digest();
368 if let Some(previous_digest) = previous_external_digest
369 && previous_digest >= digest
370 {
371 return Err(MastForestError::InvalidNodeOrder {
372 node_id,
373 reason: "external node digests must be strictly increasing".into(),
374 });
375 }
376 previous_external_digest = Some(digest);
377 }
378
379 let mut ordered_ids = external_ids;
380 let mut ordered = vec![false; node_count];
381 ordered_ids.extend(basic_block_ids);
382 for &node_id in &ordered_ids {
383 ordered[node_id.to_usize()] = true;
384 }
385
386 let mut unresolved_child_counts = vec![0usize; node_count];
390 let mut parents_by_child = vec![Vec::new(); node_count];
391 for &node_id in &internal_ids {
392 nodes[node_id].for_each_child(|child_id| {
393 if child_id.to_usize() < node_count && !ordered[child_id.to_usize()] {
394 unresolved_child_counts[node_id.to_usize()] += 1;
395 parents_by_child[child_id.to_usize()].push(node_id);
396 }
397 });
398 }
399
400 for &node_id in &internal_ids {
401 let mut invalid_child = None;
402 nodes[node_id].for_each_child(|child_id| {
403 if child_id.to_usize() >= node_count {
404 invalid_child = Some(child_id);
405 }
406 });
407
408 if let Some(child_id) = invalid_child {
409 return Err(MastForestError::NodeIdOverflow(child_id, node_count));
410 }
411 }
412
413 let mut ready_internal_ids = BTreeSet::new();
414 for &node_id in &internal_ids {
415 if unresolved_child_counts[node_id.to_usize()] == 0 {
416 ready_internal_ids.insert(node_id);
417 }
418 }
419
420 let mut ordered_internal_count = 0;
421 while let Some(node_id) = ready_internal_ids.pop_first() {
422 ordered[node_id.to_usize()] = true;
423 ordered_ids.push(node_id);
424 ordered_internal_count += 1;
425
426 for parent_id in core::mem::take(&mut parents_by_child[node_id.to_usize()]) {
427 let count = &mut unresolved_child_counts[parent_id.to_usize()];
428 *count = count.checked_sub(1).expect("ready child must have a pending parent");
429 if *count == 0 {
430 ready_internal_ids.insert(parent_id);
431 }
432 }
433 }
434
435 if ordered_internal_count != internal_ids.len() {
436 let node_id = internal_ids
437 .into_iter()
438 .find(|node_id| !ordered[node_id.to_usize()])
439 .expect("internal cycle must contain a pending node");
440 return Err(MastForestError::InvalidNodeOrder {
441 node_id,
442 reason: "internal nodes must form an acyclic child-before-parent graph".into(),
443 });
444 }
445
446 Ok(ordered_ids)
447}
448
449#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
450pub(in crate::mast) enum MastNodeOrderClass {
451 External,
452 BasicBlock,
453 Internal,
454}
455
456fn validate_dense_node_order(
457 nodes: &IndexVec<MastNodeId, MastNode>,
458) -> Result<(), MastForestError> {
459 let mut previous_class = MastNodeOrderClass::External;
460 let mut previous_external_digest = None;
461
462 for (node_index, node) in nodes.iter().enumerate() {
463 let node_id = MastNodeId::new_unchecked(node_index as u32);
464 let node_class = node.order_class();
465
466 if node_class < previous_class {
467 return Err(MastForestError::InvalidNodeOrder {
468 node_id,
469 reason: format!("node class {node_class:?} appears after {previous_class:?}"),
470 });
471 }
472 previous_class = node_class;
473
474 if let MastNode::External(external_node) = node {
475 let digest = external_node.digest();
476 if let Some(previous_digest) = previous_external_digest
477 && previous_digest >= digest
478 {
479 return Err(MastForestError::InvalidNodeOrder {
480 node_id,
481 reason: "external node digests must be strictly increasing".into(),
482 });
483 }
484 previous_external_digest = Some(digest);
485 }
486
487 let mut forward_child = None;
488 node.for_each_child(|child_id| {
489 if child_id.0 >= node_id.0 {
490 forward_child = Some(child_id);
491 }
492 });
493 if let Some(child_id) = forward_child {
494 return Err(MastForestError::ForwardReference(node_id, child_id));
495 }
496 }
497
498 Ok(())
499}
500
501impl PartialEq for MastForest {
504 fn eq(&self, other: &Self) -> bool {
505 self.nodes == other.nodes
506 && self.roots == other.roots
507 && self.advice_map == other.advice_map
508 }
509}
510
511impl Eq for MastForest {}
512
513impl MastForest {
516 const MAX_NODES: usize = (1 << 30) - 1;
518
519 #[cfg(any(test, feature = "arbitrary"))]
527 pub fn make_root(&mut self, new_root_id: MastNodeId) {
528 assert!(new_root_id.to_usize() < self.nodes.len());
529
530 if !self.roots.contains(&new_root_id) {
531 self.roots.push(new_root_id);
532 self.commitment = self.compute_mast_forest_commitment();
533 }
534 }
535
536 #[cfg(test)]
544 pub fn remove_nodes(
545 &mut self,
546 nodes_to_remove: &BTreeSet<MastNodeId>,
547 ) -> BTreeMap<MastNodeId, MastNodeId> {
548 if nodes_to_remove.is_empty() {
549 return BTreeMap::new();
550 }
551
552 self.assert_nodes_to_remove_are_orphaned(nodes_to_remove);
553
554 let old_nodes = core::mem::replace(&mut self.nodes, IndexVec::new());
555 let old_root_ids = core::mem::take(&mut self.roots);
556 let (retained_nodes, id_remappings) = remove_nodes(old_nodes.into_inner(), nodes_to_remove);
557
558 self.remap_and_add_nodes(retained_nodes, &id_remappings);
559 self.remap_and_add_roots(old_root_ids, &id_remappings);
560
561 self.commitment = self.compute_mast_forest_commitment();
562
563 id_remappings
564 }
565
566 pub fn merge<'forest>(
613 forests: impl IntoIterator<Item = &'forest MastForest>,
614 ) -> Result<(MastForest, MastForestRootMap), MastForestError> {
615 MastForestMerger::merge(forests)
616 }
617}
618
619impl MastForest {
622 #[cfg(test)]
623 fn assert_nodes_to_remove_are_orphaned(&self, nodes_to_remove: &BTreeSet<MastNodeId>) {
624 for (node_idx, node) in self.nodes.iter().enumerate() {
625 let node_id = MastNodeId::new_unchecked(node_idx.try_into().expect("too many nodes"));
626 if nodes_to_remove.contains(&node_id) {
627 continue;
628 }
629
630 node.for_each_child(|child_id| {
631 assert!(
632 !nodes_to_remove.contains(&child_id),
633 "cannot remove node {child_id:?}; retained node {node_id:?} references it"
634 );
635 });
636 }
637 }
638
639 #[cfg(test)]
645 fn remap_and_add_nodes(
646 &mut self,
647 nodes_to_add: Vec<MastNode>,
648 id_remappings: &BTreeMap<MastNodeId, MastNodeId>,
649 ) {
650 assert!(self.nodes.is_empty());
651 let node_builders =
652 nodes_to_add.into_iter().map(|node| node.to_builder(self)).collect::<Vec<_>>();
653
654 for live_node_builder in node_builders {
657 let node = live_node_builder.remap_children(id_remappings).build_linked().unwrap();
658 self.nodes.push(node).unwrap();
659 }
660 }
661
662 #[cfg(test)]
667 fn remap_and_add_roots(
668 &mut self,
669 old_root_ids: Vec<MastNodeId>,
670 id_remappings: &BTreeMap<MastNodeId, MastNodeId>,
671 ) {
672 assert!(self.roots.is_empty());
673
674 for old_root_id in old_root_ids {
675 if let Some(new_root_id) = id_remappings.get(&old_root_id).copied() {
676 self.make_root(new_root_id);
677 }
678 }
679 }
680}
681
682#[cfg(test)]
685fn remove_nodes(
686 mast_nodes: Vec<MastNode>,
687 nodes_to_remove: &BTreeSet<MastNodeId>,
688) -> (Vec<MastNode>, BTreeMap<MastNodeId, MastNodeId>) {
689 assert!(mast_nodes.len() < u32::MAX as usize);
691
692 let mut retained_nodes = Vec::with_capacity(mast_nodes.len());
693 let mut id_remappings = BTreeMap::new();
694
695 for (old_node_index, old_node) in mast_nodes.into_iter().enumerate() {
696 let old_node_id: MastNodeId = MastNodeId(old_node_index as u32);
697
698 if !nodes_to_remove.contains(&old_node_id) {
699 let new_node_id: MastNodeId = MastNodeId(retained_nodes.len() as u32);
700 id_remappings.insert(old_node_id, new_node_id);
701
702 retained_nodes.push(old_node);
703 }
704 }
705
706 (retained_nodes, id_remappings)
707}
708
709fn empty_mast_forest_commitment() -> MastForestCommitment {
710 let interface_commitment = Poseidon2::merge_many(&[]);
711 let dependency_commitment = Poseidon2::merge_many(&[]);
712 let advice_commitment = AdviceMap::default().commitment();
713 MastForestCommitment::new(interface_commitment, dependency_commitment, advice_commitment)
714}
715
716fn compute_nodes_commitment(
717 nodes: &IndexVec<MastNodeId, MastNode>,
718 node_ids: &[MastNodeId],
719) -> Word {
720 let mut digests: Vec<Word> = node_ids.iter().map(|&id| nodes[id].digest()).collect();
721 digests.sort_unstable();
722 Poseidon2::merge_many(&digests)
723}
724
725fn compute_dependency_commitment(nodes: &IndexVec<MastNodeId, MastNode>) -> Word {
726 let mut digests: Vec<Word> = nodes
727 .iter()
728 .filter(|node| node.is_external())
729 .map(MastNodeExt::digest)
730 .collect();
731 digests.sort_unstable();
732 Poseidon2::merge_many(&digests)
733}
734
735impl MastForestCommitment {
736 fn new(
737 interface_commitment: Word,
738 dependency_commitment: Word,
739 advice_commitment: Word,
740 ) -> Self {
741 let commitment = Poseidon2::merge_many(&[
742 interface_commitment,
743 dependency_commitment,
744 advice_commitment,
745 ]);
746 Self {
747 commitment,
748 interface_commitment,
749 dependency_commitment,
750 advice_commitment,
751 }
752 }
753}
754
755fn compute_mast_forest_commitment(
756 nodes: &IndexVec<MastNodeId, MastNode>,
757 roots: &[MastNodeId],
758 advice_map: &AdviceMap,
759) -> MastForestCommitment {
760 let interface_commitment = compute_nodes_commitment(nodes, roots);
761 let dependency_commitment = compute_dependency_commitment(nodes);
762 let advice_commitment = advice_map.commitment();
763 MastForestCommitment::new(interface_commitment, dependency_commitment, advice_commitment)
764}
765
766impl MastForest {
769 #[inline(always)]
774 pub fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
775 self.nodes.get(node_id)
776 }
777
778 #[inline(always)]
780 pub fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
781 self.roots.iter().find(|&&root_id| self[root_id].digest() == digest).copied()
782 }
783
784 pub fn is_procedure_root(&self, node_id: MastNodeId) -> bool {
786 self.roots.contains(&node_id)
787 }
788
789 pub fn is_procedure_root_with_exact_digest(&self, node_id: MastNodeId, digest: Word) -> bool {
795 self.is_procedure_root(node_id) && self[node_id].digest() == digest
796 }
797
798 pub fn procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
800 self.roots.iter().map(|&root_id| self[root_id].digest())
801 }
802
803 pub fn local_procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
807 self.roots.iter().filter_map(|&root_id| {
808 let node = &self[root_id];
809 if node.is_external() { None } else { Some(node.digest()) }
810 })
811 }
812
813 pub fn procedure_roots(&self) -> &[MastNodeId] {
815 &self.roots
816 }
817
818 pub fn num_procedures(&self) -> u32 {
820 self.roots
821 .len()
822 .try_into()
823 .expect("MAST forest contains more than 2^32 procedures.")
824 }
825
826 pub fn compute_nodes_commitment<'a>(
831 &self,
832 node_ids: impl IntoIterator<Item = &'a MastNodeId>,
833 ) -> Word {
834 let node_ids = node_ids.into_iter().copied().collect::<Vec<_>>();
835 compute_nodes_commitment(&self.nodes, &node_ids)
836 }
837
838 pub fn interface_commitment(&self) -> Word {
843 self.commitment.interface_commitment
844 }
845
846 pub fn dependency_commitment(&self) -> Word {
851 self.commitment.dependency_commitment
852 }
853
854 pub fn advice_commitment(&self) -> Word {
858 self.commitment.advice_commitment
859 }
860
861 fn compute_mast_forest_commitment(&self) -> MastForestCommitment {
862 compute_mast_forest_commitment(&self.nodes, &self.roots, &self.advice_map)
863 }
864
865 pub fn commitment(&self) -> Word {
869 self.commitment.commitment
870 }
871
872 pub fn num_nodes(&self) -> u32 {
874 self.nodes.len() as u32
875 }
876
877 pub fn nodes(&self) -> &[MastNode] {
879 self.nodes.as_slice()
880 }
881
882 pub fn advice_map(&self) -> &AdviceMap {
883 &self.advice_map
884 }
885
886 pub fn with_advice_map(mut self, advice_map: AdviceMap) -> Self {
888 self.advice_map.extend(advice_map);
889 self.commitment = self.compute_mast_forest_commitment();
890 self
891 }
892
893 pub fn write_hashless<W: ByteWriter>(&self, target: &mut W) {
904 serialization::write_hashless_into(self, target);
905 }
906}
907
908impl MastForest {
910 pub(in crate::mast) fn validate_dense_node_order(&self) -> Result<(), MastForestError> {
911 validate_dense_node_order(&self.nodes)
912 }
913
914 fn validate_basic_block_invariants(&self) -> Result<(), MastForestError> {
915 for (node_id_idx, node) in self.nodes.iter().enumerate() {
916 let node_id =
917 MastNodeId::new_unchecked(node_id_idx.try_into().expect("too many nodes"));
918 if let MastNode::Block(basic_block) = node {
919 basic_block.validate_batch_invariants().map_err(|error_msg| {
920 MastForestError::InvalidBatchPadding(node_id, error_msg)
921 })?;
922 }
923 }
924
925 Ok(())
926 }
927
928 pub fn validate(&self) -> Result<(), MastForestError> {
939 self.validate_basic_block_invariants()?;
940 Ok(())
941 }
942
943 fn validate_node_hashes(&self) -> Result<(), MastForestError> {
948 let computed_hashes = self.compute_node_hashes()?;
949 for (node_idx, (node, computed_digest)) in
950 self.nodes.iter().zip(computed_hashes).enumerate()
951 {
952 let expected_digest = node.digest();
953 if expected_digest != computed_digest {
954 return Err(MastForestError::HashMismatch {
955 node_id: MastNodeId::new_unchecked(node_idx as u32),
956 expected: expected_digest,
957 computed: computed_digest,
958 });
959 }
960 }
961
962 Ok(())
963 }
964
965 fn compute_node_hashes(&self) -> Result<Vec<Word>, MastForestError> {
974 use crate::chiplets::hasher;
975
976 fn check_no_forward_ref(
978 node_id: MastNodeId,
979 child_id: MastNodeId,
980 ) -> Result<(), MastForestError> {
981 if child_id.0 >= node_id.0 {
982 return Err(MastForestError::ForwardReference(node_id, child_id));
983 }
984 Ok(())
985 }
986
987 let mut computed_hashes = Vec::with_capacity(self.nodes.len());
988 for (node_idx, node) in self.nodes.iter().enumerate() {
989 let node_id = MastNodeId::new_unchecked(node_idx as u32);
990
991 let computed_digest = match node {
993 MastNode::Block(block) => {
994 let op_groups: Vec<Felt> =
995 block.op_batches().iter().flat_map(|batch| *batch.groups()).collect();
996 hasher::hash_elements(&op_groups)
997 },
998 MastNode::Join(join) => {
999 let left_id = join.first();
1000 let right_id = join.second();
1001 check_no_forward_ref(node_id, left_id)?;
1002 check_no_forward_ref(node_id, right_id)?;
1003
1004 let left_digest = computed_hashes[left_id.0 as usize];
1005 let right_digest = computed_hashes[right_id.0 as usize];
1006 hasher::merge_in_domain(&[left_digest, right_digest], JoinNode::DOMAIN)
1007 },
1008 MastNode::Split(split) => {
1009 let true_id = split.on_true();
1010 let false_id = split.on_false();
1011 check_no_forward_ref(node_id, true_id)?;
1012 check_no_forward_ref(node_id, false_id)?;
1013
1014 let true_digest = computed_hashes[true_id.0 as usize];
1015 let false_digest = computed_hashes[false_id.0 as usize];
1016 hasher::merge_in_domain(&[true_digest, false_digest], SplitNode::DOMAIN)
1017 },
1018 MastNode::Loop(loop_node) => {
1019 let body_id = loop_node.body();
1020 check_no_forward_ref(node_id, body_id)?;
1021
1022 let body_digest = computed_hashes[body_id.0 as usize];
1023 hasher::merge_in_domain(&[body_digest, Word::default()], LoopNode::DOMAIN)
1024 },
1025 MastNode::Call(call) => {
1026 let callee_id = call.callee();
1027 check_no_forward_ref(node_id, callee_id)?;
1028
1029 let callee_digest = computed_hashes[callee_id.0 as usize];
1030 let domain = if call.is_syscall() {
1031 CallNode::SYSCALL_DOMAIN
1032 } else {
1033 CallNode::CALL_DOMAIN
1034 };
1035 hasher::merge_in_domain(&[callee_digest, Word::default()], domain)
1036 },
1037 MastNode::Dyn(dyn_node) => {
1038 if dyn_node.is_dyncall() {
1039 DynNode::DYNCALL_DEFAULT_DIGEST
1040 } else {
1041 DynNode::DYN_DEFAULT_DIGEST
1042 }
1043 },
1044 MastNode::External(_) => {
1045 node.digest()
1047 },
1048 };
1049
1050 computed_hashes.push(computed_digest);
1051 }
1052
1053 Ok(computed_hashes)
1054 }
1055}
1056
1057impl Index<MastNodeId> for MastForest {
1061 type Output = MastNode;
1062
1063 #[inline(always)]
1064 fn index(&self, node_id: MastNodeId) -> &Self::Output {
1065 &self.nodes[node_id]
1066 }
1067}
1068
1069pub trait ExecutableMastForest {
1079 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode>;
1082
1083 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word>;
1092
1093 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId>;
1095
1096 fn advice_map(&self) -> &AdviceMap;
1098}
1099
1100impl ExecutableMastForest for MastForest {
1101 #[inline(always)]
1102 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
1103 MastForest::get_node_by_id(self, node_id)
1104 }
1105
1106 #[inline(always)]
1107 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word> {
1108 MastForest::get_node_by_id(self, node_id).map(MastNodeExt::digest)
1109 }
1110
1111 #[inline(always)]
1112 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
1113 MastForest::find_procedure_root(self, digest)
1114 }
1115
1116 #[inline(always)]
1117 fn advice_map(&self) -> &AdviceMap {
1118 MastForest::advice_map(self)
1119 }
1120}
1121
1122impl<T> Index<MastNodeId> for Arc<T>
1126where
1127 T: Index<MastNodeId, Output = MastNode> + ?Sized,
1128{
1129 type Output = MastNode;
1130
1131 #[inline(always)]
1132 fn index(&self, node_id: MastNodeId) -> &Self::Output {
1133 &(**self)[node_id]
1134 }
1135}
1136
1137impl<T: ExecutableMastForest + ?Sized> ExecutableMastForest for Arc<T> {
1138 #[inline(always)]
1139 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
1140 T::get_node_by_id(self, node_id)
1141 }
1142
1143 #[inline(always)]
1144 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word> {
1145 T::get_digest_by_id(self, node_id)
1146 }
1147
1148 #[inline(always)]
1149 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
1150 T::find_procedure_root(self, digest)
1151 }
1152
1153 #[inline(always)]
1154 fn advice_map(&self) -> &AdviceMap {
1155 T::advice_map(self)
1156 }
1157}
1158
1159#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1169#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1170#[cfg_attr(feature = "serde", serde(transparent))]
1171pub struct MastNodeId(u32);
1172
1173pub type Remapping = BTreeMap<MastNodeId, MastNodeId>;
1175
1176impl MastNodeId {
1177 pub fn from_u32_safe(
1182 value: u32,
1183 mast_forest: &MastForest,
1184 ) -> Result<Self, DeserializationError> {
1185 Self::from_u32_with_node_count(value, mast_forest.nodes.len())
1186 }
1187
1188 pub fn new_unchecked(value: u32) -> Self {
1190 Self(value)
1191 }
1192
1193 pub(super) fn from_u32_with_node_count(
1207 id: u32,
1208 node_count: usize,
1209 ) -> Result<Self, DeserializationError> {
1210 if (id as usize) < node_count {
1211 Ok(Self(id))
1212 } else {
1213 Err(DeserializationError::InvalidValue(format!(
1214 "Invalid deserialized MAST node ID '{id}', but {node_count} is the number of nodes in the forest",
1215 )))
1216 }
1217 }
1218
1219 pub fn remap(&self, remapping: &Remapping) -> Self {
1221 *remapping.get(self).unwrap_or(self)
1222 }
1223}
1224
1225impl From<u32> for MastNodeId {
1226 fn from(value: u32) -> Self {
1227 MastNodeId::new_unchecked(value)
1228 }
1229}
1230
1231impl Idx for MastNodeId {}
1232
1233impl From<MastNodeId> for u32 {
1234 fn from(value: MastNodeId) -> Self {
1235 value.0
1236 }
1237}
1238
1239impl Serializable for MastNodeId {
1240 fn write_into<W: ByteWriter>(&self, target: &mut W) {
1241 Serializable::write_into(&self.0, target);
1242 }
1243}
1244
1245impl fmt::Display for MastNodeId {
1246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1247 write!(f, "MastNodeId({})", self.0)
1248 }
1249}
1250
1251#[cfg(any(test, feature = "arbitrary"))]
1252impl Arbitrary for MastNodeId {
1253 type Parameters = ();
1254
1255 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1256 use proptest::prelude::*;
1257 any::<u32>().prop_map(MastNodeId).boxed()
1258 }
1259
1260 type Strategy = BoxedStrategy<Self>;
1261}
1262
1263pub struct SubtreeIterator<'a> {
1268 forest: &'a MastForest,
1269 discovered: Vec<MastNodeId>,
1270 unvisited: Vec<MastNodeId>,
1271}
1272impl<'a> SubtreeIterator<'a> {
1273 pub fn new(root: &MastNodeId, forest: &'a MastForest) -> Self {
1274 let discovered = vec![];
1275 let unvisited = vec![*root];
1276 SubtreeIterator { forest, discovered, unvisited }
1277 }
1278}
1279impl Iterator for SubtreeIterator<'_> {
1280 type Item = MastNodeId;
1281 fn next(&mut self) -> Option<MastNodeId> {
1282 while let Some(id) = self.unvisited.pop() {
1283 let node = &self.forest[id];
1284 if !node.has_children() {
1285 return Some(id);
1286 } else {
1287 self.discovered.push(id);
1288 node.append_children_to(&mut self.unvisited);
1289 }
1290 }
1291 self.discovered.pop()
1292 }
1293}
1294
1295pub fn error_code_from_msg(msg: impl AsRef<str>) -> Felt {
1298 hash_string_to_word(msg.as_ref())[0]
1300}
1301
1302#[derive(Debug, thiserror::Error, PartialEq, Eq)]
1307pub enum MastForestError {
1308 #[error("MAST forest node count exceeds the maximum of {} nodes", MastForest::MAX_NODES)]
1309 TooManyNodes,
1310 #[error("node id {0} is greater than or equal to forest length {1}")]
1311 NodeIdOverflow(MastNodeId, usize),
1312 #[error("basic block cannot be created from an empty list of operations")]
1313 EmptyBasicBlock,
1314 #[error("advice map key {0} already exists when merging forests")]
1315 AdviceMapKeyCollisionOnMerge(Word),
1316 #[error("digest is required for deserialization")]
1317 DigestRequiredForDeserialization,
1318 #[error("invalid batch in basic block node {0:?}: {1}")]
1319 InvalidBatchPadding(MastNodeId, String),
1320 #[error("invalid node order at {node_id:?}: {reason}")]
1321 InvalidNodeOrder { node_id: MastNodeId, reason: String },
1322 #[error(
1323 "node {0:?} references child {1:?} which comes after it in the forest (forward reference)"
1324 )]
1325 ForwardReference(MastNodeId, MastNodeId),
1326 #[error("hash mismatch for node {node_id:?}: expected {expected:?}, computed {computed:?}")]
1327 HashMismatch {
1328 node_id: MastNodeId,
1329 expected: Word,
1330 computed: Word,
1331 },
1332 #[error("deserialization failed: {0}")]
1333 Deserialization(DeserializationError),
1334}
1335
1336#[cfg(feature = "serde")]
1338impl Serialize for MastForest {
1339 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1340 where
1341 S: serde::Serializer,
1342 {
1343 let bytes = Serializable::to_bytes(self);
1344 serializer.serialize_bytes(&bytes)
1345 }
1346}
1347
1348#[cfg(feature = "serde")]
1349impl<'de> Deserialize<'de> for MastForest {
1350 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1351 where
1352 D: serde::Deserializer<'de>,
1353 {
1354 let bytes = Vec::<u8>::deserialize(deserializer)?;
1356 let mut slice_reader = SliceReader::new(&bytes);
1357 Deserializable::read_from(&mut slice_reader).map_err(serde::de::Error::custom)
1358 }
1359}