1#[cfg(test)]
44use alloc::collections::BTreeSet;
45use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
46use core::{fmt, ops::Index};
47
48use dense_order::{
49 canonicalize_parts, validate_dense_node_order, validate_mast_forest_parts_bounds,
50};
51#[cfg(any(test, feature = "arbitrary"))]
52use proptest::prelude::*;
53
54mod dense_order;
55mod node;
56#[cfg(any(test, feature = "arbitrary"))]
57pub use node::arbitrary;
58pub(crate) use node::collect_immediate_placements;
59pub use node::{
60 BasicBlockNode, BasicBlockNodeBuilder, CallNode, CallNodeBuilder, DynNode, DynNodeBuilder,
61 ExternalNode, ExternalNodeBuilder, JoinNode, JoinNodeBuilder, LoopNode, LoopNodeBuilder,
62 MastForestContributor, MastNode, MastNodeBuilder, MastNodeContext, MastNodeExt, OP_BATCH_SIZE,
63 OP_GROUP_SIZE, OpBatch, SplitNode, SplitNodeBuilder,
64};
65
66use crate::{
67 Felt, Word,
68 advice::AdviceMap,
69 crypto::hash::Poseidon2,
70 serde::{ByteWriter, Deserializable, DeserializationError, Serializable},
71 utils::{DenseIdMap, Idx, IndexVec, hash_string_to_word},
72};
73
74mod serialization;
75pub use serialization::{
76 AdviceMapView, AdviceValueView, MastForestReadMode, MastForestReadView, MastForestView,
77 MastForestWireView, MastNodeEntry, MastNodeInfo,
78};
79
80mod dense_builder;
81pub use dense_builder::DenseMastForestBuilder;
82
83mod untrusted;
84pub use untrusted::{UntrustedMastForest, UntrustedMastForestReadOptions};
85
86mod merger;
87pub(crate) use merger::MastForestMerger;
88pub use merger::MastForestRootMap;
89
90mod multi_forest_node_iterator;
91pub(crate) use multi_forest_node_iterator::*;
92
93mod node_builder_utils;
94pub use node_builder_utils::build_node_with_remapped_ids;
95
96mod sparse;
97pub use sparse::{MastForestId, SparseMastForest, SparseMastForestBuilder, VisitKind};
98
99#[cfg(test)]
100mod tests;
101
102#[derive(Clone, Debug, Default)]
122#[cfg_attr(
123 all(feature = "arbitrary", test),
124 miden_test_serialization_macros::serialization_test
125)]
126pub struct MastForest {
127 nodes: IndexVec<MastNodeId, MastNode>,
129
130 roots: Vec<MastNodeId>,
132
133 advice_map: AdviceMap,
135
136 commitment: MastForestCommitment,
138}
139
140#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
151struct MastForestCommitment {
152 commitment: Word,
154
155 interface_commitment: Word,
157
158 dependency_commitment: Word,
160
161 advice_commitment: Word,
163}
164
165pub(crate) struct MastForestParts {
167 pub nodes: IndexVec<MastNodeId, MastNode>,
168 pub roots: Vec<MastNodeId>,
169 pub advice_map: AdviceMap,
170}
171
172impl MastForest {
175 pub fn new() -> Self {
177 Self {
178 nodes: IndexVec::new(),
179 roots: Vec::new(),
180 advice_map: AdviceMap::default(),
181 commitment: empty_mast_forest_commitment(),
182 }
183 }
184
185 #[doc(hidden)]
189 pub fn from_raw_parts(
190 nodes: IndexVec<MastNodeId, MastNode>,
191 roots: Vec<MastNodeId>,
192 advice_map: AdviceMap,
193 ) -> Result<Self, MastForestError> {
194 Self::from_parts(MastForestParts { nodes, roots, advice_map })
195 }
196
197 #[doc(hidden)]
203 pub fn from_raw_parts_with_id_map(
204 nodes: IndexVec<MastNodeId, MastNode>,
205 roots: Vec<MastNodeId>,
206 advice_map: AdviceMap,
207 ) -> Result<(Self, DenseIdMap<MastNodeId, MastNodeId>), MastForestError> {
208 Self::from_parts_with_id_map(MastForestParts { nodes, roots, advice_map })
209 }
210
211 pub(crate) fn from_parts(parts: MastForestParts) -> Result<Self, MastForestError> {
213 Self::from_parts_with_id_map(parts).map(|(forest, _remapping)| forest)
214 }
215
216 pub(crate) fn from_parts_with_id_map(
217 parts: MastForestParts,
218 ) -> Result<(Self, DenseIdMap<MastNodeId, MastNodeId>), MastForestError> {
219 validate_mast_forest_parts_bounds(&parts)?;
220 let (parts, id_remapping) = canonicalize_parts(parts)?;
221
222 let forest = Self {
223 commitment: compute_mast_forest_commitment(
224 &parts.nodes,
225 &parts.roots,
226 &parts.advice_map,
227 ),
228 nodes: parts.nodes,
229 roots: parts.roots,
230 advice_map: parts.advice_map,
231 };
232
233 forest.validate_dense_node_order()?;
234 forest.validate()?;
235 forest.validate_node_hashes()?;
236 Ok((forest, id_remapping))
237 }
238
239 pub(in crate::mast) fn from_trusted_deserialization_parts(
240 parts: MastForestParts,
241 ) -> Result<Self, MastForestError> {
242 validate_mast_forest_parts_bounds(&parts)?;
243 validate_dense_node_order(&parts.nodes)?;
246 Ok(Self {
247 commitment: compute_mast_forest_commitment(
248 &parts.nodes,
249 &parts.roots,
250 &parts.advice_map,
251 ),
252 nodes: parts.nodes,
253 roots: parts.roots,
254 advice_map: parts.advice_map,
255 })
256 }
257}
258
259impl PartialEq for MastForest {
262 fn eq(&self, other: &Self) -> bool {
263 self.nodes == other.nodes
264 && self.roots == other.roots
265 && self.advice_map == other.advice_map
266 }
267}
268
269impl Eq for MastForest {}
270
271impl MastForest {
274 const MAX_NODES: usize = (1 << 30) - 1;
276
277 #[cfg(any(test, feature = "arbitrary"))]
285 pub fn make_root(&mut self, new_root_id: MastNodeId) {
286 assert!(new_root_id.to_usize() < self.nodes.len());
287
288 if !self.roots.contains(&new_root_id) {
289 self.roots.push(new_root_id);
290 self.commitment = self.compute_mast_forest_commitment();
291 }
292 }
293
294 #[cfg(test)]
302 fn remove_nodes(
303 &mut self,
304 nodes_to_remove: &BTreeSet<MastNodeId>,
305 ) -> BTreeMap<MastNodeId, MastNodeId> {
306 if nodes_to_remove.is_empty() {
307 return BTreeMap::new();
308 }
309
310 self.assert_nodes_to_remove_are_orphaned(nodes_to_remove);
311
312 let old_nodes = core::mem::replace(&mut self.nodes, IndexVec::new());
313 let old_root_ids = core::mem::take(&mut self.roots);
314 let (retained_nodes, id_remappings) = remove_nodes(old_nodes.into_inner(), nodes_to_remove);
315
316 self.remap_and_add_nodes(retained_nodes, &id_remappings);
317 self.remap_and_add_roots(old_root_ids, &id_remappings);
318
319 self.commitment = self.compute_mast_forest_commitment();
320
321 id_remappings
322 }
323
324 pub fn merge<'forest>(
371 forests: impl IntoIterator<Item = &'forest MastForest>,
372 ) -> Result<(MastForest, MastForestRootMap), MastForestError> {
373 MastForestMerger::merge(forests)
374 }
375}
376
377impl MastForest {
380 #[cfg(test)]
381 fn assert_nodes_to_remove_are_orphaned(&self, nodes_to_remove: &BTreeSet<MastNodeId>) {
382 for (node_idx, node) in self.nodes.iter().enumerate() {
383 let node_id = MastNodeId::new_unchecked(node_idx.try_into().expect("too many nodes"));
384 if nodes_to_remove.contains(&node_id) {
385 continue;
386 }
387
388 node.for_each_child(|child_id| {
389 assert!(
390 !nodes_to_remove.contains(&child_id),
391 "cannot remove node {child_id:?}; retained node {node_id:?} references it"
392 );
393 });
394 }
395 }
396
397 #[cfg(test)]
403 fn remap_and_add_nodes(
404 &mut self,
405 nodes_to_add: Vec<MastNode>,
406 id_remappings: &BTreeMap<MastNodeId, MastNodeId>,
407 ) {
408 assert!(self.nodes.is_empty());
409 let node_builders =
410 nodes_to_add.into_iter().map(|node| node.to_builder(self)).collect::<Vec<_>>();
411
412 for live_node_builder in node_builders {
415 let node = live_node_builder.remap_children(id_remappings).build_linked().unwrap();
416 self.nodes.push(node).unwrap();
417 }
418 }
419
420 #[cfg(test)]
425 fn remap_and_add_roots(
426 &mut self,
427 old_root_ids: Vec<MastNodeId>,
428 id_remappings: &BTreeMap<MastNodeId, MastNodeId>,
429 ) {
430 assert!(self.roots.is_empty());
431
432 for old_root_id in old_root_ids {
433 if let Some(new_root_id) = id_remappings.get(&old_root_id).copied() {
434 self.make_root(new_root_id);
435 }
436 }
437 }
438}
439
440#[cfg(test)]
443fn remove_nodes(
444 mast_nodes: Vec<MastNode>,
445 nodes_to_remove: &BTreeSet<MastNodeId>,
446) -> (Vec<MastNode>, BTreeMap<MastNodeId, MastNodeId>) {
447 assert!(mast_nodes.len() < u32::MAX as usize);
449
450 let mut retained_nodes = Vec::with_capacity(mast_nodes.len());
451 let mut id_remappings = BTreeMap::new();
452
453 for (old_node_index, old_node) in mast_nodes.into_iter().enumerate() {
454 let old_node_id: MastNodeId = MastNodeId(old_node_index as u32);
455
456 if !nodes_to_remove.contains(&old_node_id) {
457 let new_node_id: MastNodeId = MastNodeId(retained_nodes.len() as u32);
458 id_remappings.insert(old_node_id, new_node_id);
459
460 retained_nodes.push(old_node);
461 }
462 }
463
464 (retained_nodes, id_remappings)
465}
466
467fn empty_mast_forest_commitment() -> MastForestCommitment {
468 let interface_commitment = Poseidon2::merge_many(&[]);
469 let dependency_commitment = Poseidon2::merge_many(&[]);
470 let advice_commitment = AdviceMap::default().commitment();
471 MastForestCommitment::new(interface_commitment, dependency_commitment, advice_commitment)
472}
473
474fn compute_nodes_commitment(
475 nodes: &IndexVec<MastNodeId, MastNode>,
476 node_ids: &[MastNodeId],
477) -> Word {
478 let mut digests: Vec<Word> = node_ids.iter().map(|&id| nodes[id].digest()).collect();
479 digests.sort_unstable();
480 Poseidon2::merge_many(&digests)
481}
482
483fn compute_dependency_commitment(nodes: &IndexVec<MastNodeId, MastNode>) -> Word {
484 let mut digests: Vec<Word> = nodes
485 .iter()
486 .filter(|node| node.is_external())
487 .map(MastNodeExt::digest)
488 .collect();
489 digests.sort_unstable();
490 Poseidon2::merge_many(&digests)
491}
492
493impl MastForestCommitment {
494 fn new(
495 interface_commitment: Word,
496 dependency_commitment: Word,
497 advice_commitment: Word,
498 ) -> Self {
499 let commitment = Poseidon2::merge_many(&[
500 interface_commitment,
501 dependency_commitment,
502 advice_commitment,
503 ]);
504 Self {
505 commitment,
506 interface_commitment,
507 dependency_commitment,
508 advice_commitment,
509 }
510 }
511}
512
513fn compute_mast_forest_commitment(
514 nodes: &IndexVec<MastNodeId, MastNode>,
515 roots: &[MastNodeId],
516 advice_map: &AdviceMap,
517) -> MastForestCommitment {
518 let interface_commitment = compute_nodes_commitment(nodes, roots);
519 let dependency_commitment = compute_dependency_commitment(nodes);
520 let advice_commitment = advice_map.commitment();
521 MastForestCommitment::new(interface_commitment, dependency_commitment, advice_commitment)
522}
523
524impl MastForest {
527 #[inline(always)]
532 pub fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
533 self.nodes.get(node_id)
534 }
535
536 #[inline(always)]
538 pub fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
539 self.roots.iter().find(|&&root_id| self[root_id].digest() == digest).copied()
540 }
541
542 pub fn is_procedure_root(&self, node_id: MastNodeId) -> bool {
544 self.roots.contains(&node_id)
545 }
546
547 pub fn is_procedure_root_with_exact_digest(&self, node_id: MastNodeId, digest: Word) -> bool {
553 self.is_procedure_root(node_id) && self[node_id].digest() == digest
554 }
555
556 pub fn procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
558 self.roots.iter().map(|&root_id| self[root_id].digest())
559 }
560
561 pub fn local_procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
565 self.roots.iter().filter_map(|&root_id| {
566 let node = &self[root_id];
567 if node.is_external() { None } else { Some(node.digest()) }
568 })
569 }
570
571 pub fn procedure_roots(&self) -> &[MastNodeId] {
573 &self.roots
574 }
575
576 pub fn num_procedures(&self) -> u32 {
578 self.roots
579 .len()
580 .try_into()
581 .expect("MAST forest contains more than 2^32 procedures.")
582 }
583
584 pub fn compute_nodes_commitment<'a>(
589 &self,
590 node_ids: impl IntoIterator<Item = &'a MastNodeId>,
591 ) -> Word {
592 let node_ids = node_ids.into_iter().copied().collect::<Vec<_>>();
593 compute_nodes_commitment(&self.nodes, &node_ids)
594 }
595
596 pub fn interface_commitment(&self) -> Word {
601 self.commitment.interface_commitment
602 }
603
604 pub fn dependency_commitment(&self) -> Word {
609 self.commitment.dependency_commitment
610 }
611
612 pub fn advice_commitment(&self) -> Word {
616 self.commitment.advice_commitment
617 }
618
619 fn compute_mast_forest_commitment(&self) -> MastForestCommitment {
620 compute_mast_forest_commitment(&self.nodes, &self.roots, &self.advice_map)
621 }
622
623 pub fn commitment(&self) -> Word {
627 self.commitment.commitment
628 }
629
630 pub fn num_nodes(&self) -> u32 {
632 self.nodes.len() as u32
633 }
634
635 pub fn nodes(&self) -> &[MastNode] {
637 self.nodes.as_slice()
638 }
639
640 pub fn advice_map(&self) -> &AdviceMap {
641 &self.advice_map
642 }
643
644 pub fn with_advice_map(mut self, advice_map: AdviceMap) -> Self {
646 self.advice_map.extend(advice_map);
647 self.commitment = self.compute_mast_forest_commitment();
648 self
649 }
650
651 pub fn write_hashless<W: ByteWriter>(&self, target: &mut W) {
662 serialization::write_hashless_into(self, target);
663 }
664
665 pub fn hashless_size_hint(&self) -> usize {
674 self.serialized_size_hint(true)
675 }
676}
677
678impl MastForest {
680 pub(in crate::mast) fn validate_dense_node_order(&self) -> Result<(), MastForestError> {
681 validate_dense_node_order(&self.nodes)
682 }
683
684 fn validate_basic_block_invariants(&self) -> Result<(), MastForestError> {
685 for (node_id_idx, node) in self.nodes.iter().enumerate() {
686 let node_id =
687 MastNodeId::new_unchecked(node_id_idx.try_into().expect("too many nodes"));
688 if let MastNode::Block(basic_block) = node {
689 basic_block.validate_batch_invariants().map_err(|error_msg| {
690 MastForestError::InvalidBatchPadding(node_id, error_msg)
691 })?;
692 }
693 }
694
695 Ok(())
696 }
697
698 pub fn validate(&self) -> Result<(), MastForestError> {
709 self.validate_basic_block_invariants()?;
710 Ok(())
711 }
712
713 fn validate_node_hashes(&self) -> Result<(), MastForestError> {
718 let computed_hashes = self.compute_node_hashes()?;
719 for (node_idx, (node, computed_digest)) in
720 self.nodes.iter().zip(computed_hashes).enumerate()
721 {
722 let expected_digest = node.digest();
723 if expected_digest != computed_digest {
724 return Err(MastForestError::HashMismatch {
725 node_id: MastNodeId::new_unchecked(node_idx as u32),
726 expected: expected_digest,
727 computed: computed_digest,
728 });
729 }
730 }
731
732 Ok(())
733 }
734
735 fn compute_node_hashes(&self) -> Result<Vec<Word>, MastForestError> {
744 use crate::chiplets::hasher;
745
746 fn check_no_forward_ref(
748 node_id: MastNodeId,
749 child_id: MastNodeId,
750 ) -> Result<(), MastForestError> {
751 if child_id.0 >= node_id.0 {
752 return Err(MastForestError::ForwardReference(node_id, child_id));
753 }
754 Ok(())
755 }
756
757 let mut computed_hashes = Vec::with_capacity(self.nodes.len());
758 for (node_idx, node) in self.nodes.iter().enumerate() {
759 let node_id = MastNodeId::new_unchecked(node_idx as u32);
760
761 let computed_digest = match node {
763 MastNode::Block(block) => {
764 let op_groups: Vec<Felt> =
765 block.op_batches().iter().flat_map(|batch| *batch.groups()).collect();
766 hasher::hash_elements(&op_groups)
767 },
768 MastNode::Join(join) => {
769 let left_id = join.first();
770 let right_id = join.second();
771 check_no_forward_ref(node_id, left_id)?;
772 check_no_forward_ref(node_id, right_id)?;
773
774 let left_digest = computed_hashes[left_id.0 as usize];
775 let right_digest = computed_hashes[right_id.0 as usize];
776 hasher::merge_in_domain(&[left_digest, right_digest], JoinNode::DOMAIN)
777 },
778 MastNode::Split(split) => {
779 let true_id = split.on_true();
780 let false_id = split.on_false();
781 check_no_forward_ref(node_id, true_id)?;
782 check_no_forward_ref(node_id, false_id)?;
783
784 let true_digest = computed_hashes[true_id.0 as usize];
785 let false_digest = computed_hashes[false_id.0 as usize];
786 hasher::merge_in_domain(&[true_digest, false_digest], SplitNode::DOMAIN)
787 },
788 MastNode::Loop(loop_node) => {
789 let body_id = loop_node.body();
790 check_no_forward_ref(node_id, body_id)?;
791
792 let body_digest = computed_hashes[body_id.0 as usize];
793 hasher::merge_in_domain(&[body_digest, Word::default()], LoopNode::DOMAIN)
794 },
795 MastNode::Call(call) => {
796 let callee_id = call.callee();
797 check_no_forward_ref(node_id, callee_id)?;
798
799 let callee_digest = computed_hashes[callee_id.0 as usize];
800 let domain = if call.is_syscall() {
801 CallNode::SYSCALL_DOMAIN
802 } else {
803 CallNode::CALL_DOMAIN
804 };
805 hasher::merge_in_domain(&[callee_digest, Word::default()], domain)
806 },
807 MastNode::Dyn(dyn_node) => {
808 if dyn_node.is_dyncall() {
809 DynNode::DYNCALL_DEFAULT_DIGEST
810 } else {
811 DynNode::DYN_DEFAULT_DIGEST
812 }
813 },
814 MastNode::External(_) => {
815 node.digest()
817 },
818 };
819
820 computed_hashes.push(computed_digest);
821 }
822
823 Ok(computed_hashes)
824 }
825}
826
827impl Index<MastNodeId> for MastForest {
831 type Output = MastNode;
832
833 #[inline(always)]
834 fn index(&self, node_id: MastNodeId) -> &Self::Output {
835 &self.nodes[node_id]
836 }
837}
838
839pub trait ExecutableMastForest {
849 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode>;
852
853 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word>;
862
863 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId>;
865
866 fn advice_map(&self) -> &AdviceMap;
868}
869
870impl ExecutableMastForest for MastForest {
871 #[inline(always)]
872 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
873 MastForest::get_node_by_id(self, node_id)
874 }
875
876 #[inline(always)]
877 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word> {
878 MastForest::get_node_by_id(self, node_id).map(MastNodeExt::digest)
879 }
880
881 #[inline(always)]
882 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
883 MastForest::find_procedure_root(self, digest)
884 }
885
886 #[inline(always)]
887 fn advice_map(&self) -> &AdviceMap {
888 MastForest::advice_map(self)
889 }
890}
891
892impl<T> Index<MastNodeId> for Arc<T>
896where
897 T: Index<MastNodeId, Output = MastNode> + ?Sized,
898{
899 type Output = MastNode;
900
901 #[inline(always)]
902 fn index(&self, node_id: MastNodeId) -> &Self::Output {
903 &(**self)[node_id]
904 }
905}
906
907impl<T: ExecutableMastForest + ?Sized> ExecutableMastForest for Arc<T> {
908 #[inline(always)]
909 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
910 T::get_node_by_id(self, node_id)
911 }
912
913 #[inline(always)]
914 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word> {
915 T::get_digest_by_id(self, node_id)
916 }
917
918 #[inline(always)]
919 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
920 T::find_procedure_root(self, digest)
921 }
922
923 #[inline(always)]
924 fn advice_map(&self) -> &AdviceMap {
925 T::advice_map(self)
926 }
927}
928
929#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
939pub struct MastNodeId(u32);
940
941pub type Remapping = BTreeMap<MastNodeId, MastNodeId>;
943
944impl MastNodeId {
945 pub fn from_u32_safe(
950 value: u32,
951 mast_forest: &MastForest,
952 ) -> Result<Self, DeserializationError> {
953 Self::from_u32_with_node_count(value, mast_forest.nodes.len())
954 }
955
956 pub fn new_unchecked(value: u32) -> Self {
958 Self(value)
959 }
960
961 pub(super) fn from_u32_with_node_count(
975 id: u32,
976 node_count: usize,
977 ) -> Result<Self, DeserializationError> {
978 if (id as usize) < node_count {
979 Ok(Self(id))
980 } else {
981 Err(DeserializationError::InvalidValue(format!(
982 "Invalid deserialized MAST node ID '{id}', but {node_count} is the number of nodes in the forest",
983 )))
984 }
985 }
986
987 pub fn remap(&self, remapping: &Remapping) -> Self {
989 *remapping.get(self).unwrap_or(self)
990 }
991}
992
993impl From<u32> for MastNodeId {
994 fn from(value: u32) -> Self {
995 MastNodeId::new_unchecked(value)
996 }
997}
998
999impl Idx for MastNodeId {}
1000
1001impl From<MastNodeId> for u32 {
1002 fn from(value: MastNodeId) -> Self {
1003 value.0
1004 }
1005}
1006
1007impl Serializable for MastNodeId {
1008 fn write_into<W: ByteWriter>(&self, target: &mut W) {
1009 Serializable::write_into(&self.0, target);
1010 }
1011}
1012
1013impl Deserializable for MastNodeId {
1014 fn read_from<R: crate::serde::ByteReader>(
1015 source: &mut R,
1016 ) -> Result<Self, DeserializationError> {
1017 Ok(Self(<u32 as Deserializable>::read_from(source)?))
1018 }
1019
1020 fn min_serialized_size() -> usize {
1021 <u32 as Deserializable>::min_serialized_size()
1022 }
1023}
1024
1025impl fmt::Display for MastNodeId {
1026 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1027 write!(f, "MastNodeId({})", self.0)
1028 }
1029}
1030
1031#[cfg(any(test, feature = "arbitrary"))]
1032impl Arbitrary for MastNodeId {
1033 type Parameters = ();
1034
1035 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1036 use proptest::prelude::*;
1037 any::<u32>().prop_map(MastNodeId).boxed()
1038 }
1039
1040 type Strategy = BoxedStrategy<Self>;
1041}
1042
1043pub struct SubtreeIterator<'a> {
1048 forest: &'a MastForest,
1049 discovered: Vec<MastNodeId>,
1050 unvisited: Vec<MastNodeId>,
1051}
1052impl<'a> SubtreeIterator<'a> {
1053 pub fn new(root: &MastNodeId, forest: &'a MastForest) -> Self {
1054 let discovered = vec![];
1055 let unvisited = vec![*root];
1056 SubtreeIterator { forest, discovered, unvisited }
1057 }
1058}
1059impl Iterator for SubtreeIterator<'_> {
1060 type Item = MastNodeId;
1061 fn next(&mut self) -> Option<MastNodeId> {
1062 while let Some(id) = self.unvisited.pop() {
1063 let node = &self.forest[id];
1064 if !node.has_children() {
1065 return Some(id);
1066 } else {
1067 self.discovered.push(id);
1068 node.append_children_to(&mut self.unvisited);
1069 }
1070 }
1071 self.discovered.pop()
1072 }
1073}
1074
1075pub fn error_code_from_msg(msg: impl AsRef<str>) -> Felt {
1078 hash_string_to_word(msg.as_ref())[0]
1080}
1081
1082#[derive(Debug, thiserror::Error, PartialEq, Eq)]
1087pub enum MastForestError {
1088 #[error("MAST forest node count exceeds the maximum of {} nodes", MastForest::MAX_NODES)]
1089 TooManyNodes,
1090 #[error("node id {0} is greater than or equal to forest length {1}")]
1091 NodeIdOverflow(MastNodeId, usize),
1092 #[error("basic block cannot be created from an empty list of operations")]
1093 EmptyBasicBlock,
1094 #[error("advice map key {0} already exists when merging forests")]
1095 AdviceMapKeyCollisionOnMerge(Word),
1096 #[error("digest is required for deserialization")]
1097 DigestRequiredForDeserialization,
1098 #[error("invalid batch in basic block node {0:?}: {1}")]
1099 InvalidBatchPadding(MastNodeId, String),
1100 #[error("invalid node order at {node_id:?}: {reason}")]
1101 InvalidNodeOrder { node_id: MastNodeId, reason: String },
1102 #[error(
1103 "node {0:?} references child {1:?} which comes after it in the forest (forward reference)"
1104 )]
1105 ForwardReference(MastNodeId, MastNodeId),
1106 #[error("hash mismatch for node {node_id:?}: expected {expected:?}, computed {computed:?}")]
1107 HashMismatch {
1108 node_id: MastNodeId,
1109 expected: Word,
1110 computed: Word,
1111 },
1112 #[error("deserialization failed: {0}")]
1113 Deserialization(DeserializationError),
1114}
1115
1116