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
666impl MastForest {
668 pub(in crate::mast) fn validate_dense_node_order(&self) -> Result<(), MastForestError> {
669 validate_dense_node_order(&self.nodes)
670 }
671
672 fn validate_basic_block_invariants(&self) -> Result<(), MastForestError> {
673 for (node_id_idx, node) in self.nodes.iter().enumerate() {
674 let node_id =
675 MastNodeId::new_unchecked(node_id_idx.try_into().expect("too many nodes"));
676 if let MastNode::Block(basic_block) = node {
677 basic_block.validate_batch_invariants().map_err(|error_msg| {
678 MastForestError::InvalidBatchPadding(node_id, error_msg)
679 })?;
680 }
681 }
682
683 Ok(())
684 }
685
686 pub fn validate(&self) -> Result<(), MastForestError> {
697 self.validate_basic_block_invariants()?;
698 Ok(())
699 }
700
701 fn validate_node_hashes(&self) -> Result<(), MastForestError> {
706 let computed_hashes = self.compute_node_hashes()?;
707 for (node_idx, (node, computed_digest)) in
708 self.nodes.iter().zip(computed_hashes).enumerate()
709 {
710 let expected_digest = node.digest();
711 if expected_digest != computed_digest {
712 return Err(MastForestError::HashMismatch {
713 node_id: MastNodeId::new_unchecked(node_idx as u32),
714 expected: expected_digest,
715 computed: computed_digest,
716 });
717 }
718 }
719
720 Ok(())
721 }
722
723 fn compute_node_hashes(&self) -> Result<Vec<Word>, MastForestError> {
732 use crate::chiplets::hasher;
733
734 fn check_no_forward_ref(
736 node_id: MastNodeId,
737 child_id: MastNodeId,
738 ) -> Result<(), MastForestError> {
739 if child_id.0 >= node_id.0 {
740 return Err(MastForestError::ForwardReference(node_id, child_id));
741 }
742 Ok(())
743 }
744
745 let mut computed_hashes = Vec::with_capacity(self.nodes.len());
746 for (node_idx, node) in self.nodes.iter().enumerate() {
747 let node_id = MastNodeId::new_unchecked(node_idx as u32);
748
749 let computed_digest = match node {
751 MastNode::Block(block) => {
752 let op_groups: Vec<Felt> =
753 block.op_batches().iter().flat_map(|batch| *batch.groups()).collect();
754 hasher::hash_elements(&op_groups)
755 },
756 MastNode::Join(join) => {
757 let left_id = join.first();
758 let right_id = join.second();
759 check_no_forward_ref(node_id, left_id)?;
760 check_no_forward_ref(node_id, right_id)?;
761
762 let left_digest = computed_hashes[left_id.0 as usize];
763 let right_digest = computed_hashes[right_id.0 as usize];
764 hasher::merge_in_domain(&[left_digest, right_digest], JoinNode::DOMAIN)
765 },
766 MastNode::Split(split) => {
767 let true_id = split.on_true();
768 let false_id = split.on_false();
769 check_no_forward_ref(node_id, true_id)?;
770 check_no_forward_ref(node_id, false_id)?;
771
772 let true_digest = computed_hashes[true_id.0 as usize];
773 let false_digest = computed_hashes[false_id.0 as usize];
774 hasher::merge_in_domain(&[true_digest, false_digest], SplitNode::DOMAIN)
775 },
776 MastNode::Loop(loop_node) => {
777 let body_id = loop_node.body();
778 check_no_forward_ref(node_id, body_id)?;
779
780 let body_digest = computed_hashes[body_id.0 as usize];
781 hasher::merge_in_domain(&[body_digest, Word::default()], LoopNode::DOMAIN)
782 },
783 MastNode::Call(call) => {
784 let callee_id = call.callee();
785 check_no_forward_ref(node_id, callee_id)?;
786
787 let callee_digest = computed_hashes[callee_id.0 as usize];
788 let domain = if call.is_syscall() {
789 CallNode::SYSCALL_DOMAIN
790 } else {
791 CallNode::CALL_DOMAIN
792 };
793 hasher::merge_in_domain(&[callee_digest, Word::default()], domain)
794 },
795 MastNode::Dyn(dyn_node) => {
796 if dyn_node.is_dyncall() {
797 DynNode::DYNCALL_DEFAULT_DIGEST
798 } else {
799 DynNode::DYN_DEFAULT_DIGEST
800 }
801 },
802 MastNode::External(_) => {
803 node.digest()
805 },
806 };
807
808 computed_hashes.push(computed_digest);
809 }
810
811 Ok(computed_hashes)
812 }
813}
814
815impl Index<MastNodeId> for MastForest {
819 type Output = MastNode;
820
821 #[inline(always)]
822 fn index(&self, node_id: MastNodeId) -> &Self::Output {
823 &self.nodes[node_id]
824 }
825}
826
827pub trait ExecutableMastForest {
837 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode>;
840
841 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word>;
850
851 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId>;
853
854 fn advice_map(&self) -> &AdviceMap;
856}
857
858impl ExecutableMastForest for MastForest {
859 #[inline(always)]
860 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
861 MastForest::get_node_by_id(self, node_id)
862 }
863
864 #[inline(always)]
865 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word> {
866 MastForest::get_node_by_id(self, node_id).map(MastNodeExt::digest)
867 }
868
869 #[inline(always)]
870 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
871 MastForest::find_procedure_root(self, digest)
872 }
873
874 #[inline(always)]
875 fn advice_map(&self) -> &AdviceMap {
876 MastForest::advice_map(self)
877 }
878}
879
880impl<T> Index<MastNodeId> for Arc<T>
884where
885 T: Index<MastNodeId, Output = MastNode> + ?Sized,
886{
887 type Output = MastNode;
888
889 #[inline(always)]
890 fn index(&self, node_id: MastNodeId) -> &Self::Output {
891 &(**self)[node_id]
892 }
893}
894
895impl<T: ExecutableMastForest + ?Sized> ExecutableMastForest for Arc<T> {
896 #[inline(always)]
897 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
898 T::get_node_by_id(self, node_id)
899 }
900
901 #[inline(always)]
902 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word> {
903 T::get_digest_by_id(self, node_id)
904 }
905
906 #[inline(always)]
907 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
908 T::find_procedure_root(self, digest)
909 }
910
911 #[inline(always)]
912 fn advice_map(&self) -> &AdviceMap {
913 T::advice_map(self)
914 }
915}
916
917#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
927pub struct MastNodeId(u32);
928
929pub type Remapping = BTreeMap<MastNodeId, MastNodeId>;
931
932impl MastNodeId {
933 pub fn from_u32_safe(
938 value: u32,
939 mast_forest: &MastForest,
940 ) -> Result<Self, DeserializationError> {
941 Self::from_u32_with_node_count(value, mast_forest.nodes.len())
942 }
943
944 pub fn new_unchecked(value: u32) -> Self {
946 Self(value)
947 }
948
949 pub(super) fn from_u32_with_node_count(
963 id: u32,
964 node_count: usize,
965 ) -> Result<Self, DeserializationError> {
966 if (id as usize) < node_count {
967 Ok(Self(id))
968 } else {
969 Err(DeserializationError::InvalidValue(format!(
970 "Invalid deserialized MAST node ID '{id}', but {node_count} is the number of nodes in the forest",
971 )))
972 }
973 }
974
975 pub fn remap(&self, remapping: &Remapping) -> Self {
977 *remapping.get(self).unwrap_or(self)
978 }
979}
980
981impl From<u32> for MastNodeId {
982 fn from(value: u32) -> Self {
983 MastNodeId::new_unchecked(value)
984 }
985}
986
987impl Idx for MastNodeId {}
988
989impl From<MastNodeId> for u32 {
990 fn from(value: MastNodeId) -> Self {
991 value.0
992 }
993}
994
995impl Serializable for MastNodeId {
996 fn write_into<W: ByteWriter>(&self, target: &mut W) {
997 Serializable::write_into(&self.0, target);
998 }
999}
1000
1001impl Deserializable for MastNodeId {
1002 fn read_from<R: crate::serde::ByteReader>(
1003 source: &mut R,
1004 ) -> Result<Self, DeserializationError> {
1005 Ok(Self(<u32 as Deserializable>::read_from(source)?))
1006 }
1007
1008 fn min_serialized_size() -> usize {
1009 <u32 as Deserializable>::min_serialized_size()
1010 }
1011}
1012
1013impl fmt::Display for MastNodeId {
1014 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1015 write!(f, "MastNodeId({})", self.0)
1016 }
1017}
1018
1019#[cfg(any(test, feature = "arbitrary"))]
1020impl Arbitrary for MastNodeId {
1021 type Parameters = ();
1022
1023 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1024 use proptest::prelude::*;
1025 any::<u32>().prop_map(MastNodeId).boxed()
1026 }
1027
1028 type Strategy = BoxedStrategy<Self>;
1029}
1030
1031pub struct SubtreeIterator<'a> {
1036 forest: &'a MastForest,
1037 discovered: Vec<MastNodeId>,
1038 unvisited: Vec<MastNodeId>,
1039}
1040impl<'a> SubtreeIterator<'a> {
1041 pub fn new(root: &MastNodeId, forest: &'a MastForest) -> Self {
1042 let discovered = vec![];
1043 let unvisited = vec![*root];
1044 SubtreeIterator { forest, discovered, unvisited }
1045 }
1046}
1047impl Iterator for SubtreeIterator<'_> {
1048 type Item = MastNodeId;
1049 fn next(&mut self) -> Option<MastNodeId> {
1050 while let Some(id) = self.unvisited.pop() {
1051 let node = &self.forest[id];
1052 if !node.has_children() {
1053 return Some(id);
1054 } else {
1055 self.discovered.push(id);
1056 node.append_children_to(&mut self.unvisited);
1057 }
1058 }
1059 self.discovered.pop()
1060 }
1061}
1062
1063pub fn error_code_from_msg(msg: impl AsRef<str>) -> Felt {
1066 hash_string_to_word(msg.as_ref())[0]
1068}
1069
1070#[derive(Debug, thiserror::Error, PartialEq, Eq)]
1075pub enum MastForestError {
1076 #[error("MAST forest node count exceeds the maximum of {} nodes", MastForest::MAX_NODES)]
1077 TooManyNodes,
1078 #[error("node id {0} is greater than or equal to forest length {1}")]
1079 NodeIdOverflow(MastNodeId, usize),
1080 #[error("basic block cannot be created from an empty list of operations")]
1081 EmptyBasicBlock,
1082 #[error("advice map key {0} already exists when merging forests")]
1083 AdviceMapKeyCollisionOnMerge(Word),
1084 #[error("digest is required for deserialization")]
1085 DigestRequiredForDeserialization,
1086 #[error("invalid batch in basic block node {0:?}: {1}")]
1087 InvalidBatchPadding(MastNodeId, String),
1088 #[error("invalid node order at {node_id:?}: {reason}")]
1089 InvalidNodeOrder { node_id: MastNodeId, reason: String },
1090 #[error(
1091 "node {0:?} references child {1:?} which comes after it in the forest (forward reference)"
1092 )]
1093 ForwardReference(MastNodeId, MastNodeId),
1094 #[error("hash mismatch for node {node_id:?}: expected {expected:?}, computed {computed:?}")]
1095 HashMismatch {
1096 node_id: MastNodeId,
1097 expected: Word,
1098 computed: Word,
1099 },
1100 #[error("deserialization failed: {0}")]
1101 Deserialization(DeserializationError),
1102}
1103
1104