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 serde::{ByteWriter, Deserializable, DeserializationError, Serializable},
70 utils::{DenseIdMap, Idx, IndexVec, hash_string_to_word},
71};
72
73mod serialization;
74pub use serialization::{
75 AdviceMapView, AdviceValueView, MastForestReadMode, MastForestReadView, MastForestView,
76 MastForestWireView, MastNodeEntry, MastNodeInfo,
77};
78
79mod dense_builder;
80pub use dense_builder::DenseMastForestBuilder;
81
82mod untrusted;
83pub use untrusted::{UntrustedMastForest, UntrustedMastForestReadOptions};
84
85mod merger;
86pub(crate) use merger::MastForestMerger;
87pub use merger::MastForestRootMap;
88
89mod multi_forest_node_iterator;
90pub(crate) use multi_forest_node_iterator::*;
91
92mod node_builder_utils;
93pub use node_builder_utils::build_node_with_remapped_ids;
94
95mod sparse;
96pub use sparse::{MastForestId, SparseMastForest, SparseMastForestBuilder, VisitKind};
97
98#[cfg(test)]
99mod tests;
100
101#[derive(Clone, Debug, Default)]
121#[cfg_attr(
122 all(feature = "arbitrary", test),
123 miden_test_serialization_macros::serialization_test
124)]
125pub struct MastForest {
126 nodes: IndexVec<MastNodeId, MastNode>,
128
129 roots: Vec<MastNodeId>,
131
132 advice_map: AdviceMap,
134
135 commitment: MastForestCommitment,
137}
138
139#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
150struct MastForestCommitment {
151 commitment: Word,
153
154 interface_commitment: Word,
156
157 dependency_commitment: Word,
159
160 advice_commitment: Word,
162}
163
164pub(crate) struct MastForestParts {
166 pub nodes: IndexVec<MastNodeId, MastNode>,
167 pub roots: Vec<MastNodeId>,
168 pub advice_map: AdviceMap,
169}
170
171impl MastForest {
174 pub fn new() -> Self {
176 Self {
177 nodes: IndexVec::new(),
178 roots: Vec::new(),
179 advice_map: AdviceMap::default(),
180 commitment: empty_mast_forest_commitment(),
181 }
182 }
183
184 #[doc(hidden)]
188 pub fn from_raw_parts(
189 nodes: IndexVec<MastNodeId, MastNode>,
190 roots: Vec<MastNodeId>,
191 advice_map: AdviceMap,
192 ) -> Result<Self, MastForestError> {
193 Self::from_parts(MastForestParts { nodes, roots, advice_map })
194 }
195
196 #[doc(hidden)]
202 pub fn from_raw_parts_with_id_map(
203 nodes: IndexVec<MastNodeId, MastNode>,
204 roots: Vec<MastNodeId>,
205 advice_map: AdviceMap,
206 ) -> Result<(Self, DenseIdMap<MastNodeId, MastNodeId>), MastForestError> {
207 Self::from_parts_with_id_map(MastForestParts { nodes, roots, advice_map })
208 }
209
210 pub(crate) fn from_parts(parts: MastForestParts) -> Result<Self, MastForestError> {
212 Self::from_parts_with_id_map(parts).map(|(forest, _remapping)| forest)
213 }
214
215 pub(crate) fn from_parts_with_id_map(
216 parts: MastForestParts,
217 ) -> Result<(Self, DenseIdMap<MastNodeId, MastNodeId>), MastForestError> {
218 validate_mast_forest_parts_bounds(&parts)?;
219 let (parts, id_remapping) = canonicalize_parts(parts)?;
220
221 let forest = Self {
222 commitment: compute_mast_forest_commitment(
223 &parts.nodes,
224 &parts.roots,
225 &parts.advice_map,
226 ),
227 nodes: parts.nodes,
228 roots: parts.roots,
229 advice_map: parts.advice_map,
230 };
231
232 forest.validate_dense_node_order()?;
233 forest.validate()?;
234 forest.validate_node_hashes()?;
235 Ok((forest, id_remapping))
236 }
237
238 pub(in crate::mast) fn from_trusted_deserialization_parts(
239 parts: MastForestParts,
240 ) -> Result<Self, MastForestError> {
241 validate_mast_forest_parts_bounds(&parts)?;
242 validate_dense_node_order(&parts.nodes)?;
245 Ok(Self {
246 commitment: compute_mast_forest_commitment(
247 &parts.nodes,
248 &parts.roots,
249 &parts.advice_map,
250 ),
251 nodes: parts.nodes,
252 roots: parts.roots,
253 advice_map: parts.advice_map,
254 })
255 }
256}
257
258impl PartialEq for MastForest {
261 fn eq(&self, other: &Self) -> bool {
262 self.nodes == other.nodes
263 && self.roots == other.roots
264 && self.advice_map == other.advice_map
265 }
266}
267
268impl Eq for MastForest {}
269
270impl MastForest {
273 const MAX_NODES: usize = (1 << 30) - 1;
275
276 #[cfg(any(test, feature = "arbitrary"))]
284 pub fn make_root(&mut self, new_root_id: MastNodeId) {
285 assert!(new_root_id.to_usize() < self.nodes.len());
286
287 if !self.roots.contains(&new_root_id) {
288 self.roots.push(new_root_id);
289 self.commitment = self.compute_mast_forest_commitment();
290 }
291 }
292
293 #[cfg(test)]
301 fn remove_nodes(
302 &mut self,
303 nodes_to_remove: &BTreeSet<MastNodeId>,
304 ) -> BTreeMap<MastNodeId, MastNodeId> {
305 if nodes_to_remove.is_empty() {
306 return BTreeMap::new();
307 }
308
309 self.assert_nodes_to_remove_are_orphaned(nodes_to_remove);
310
311 let old_nodes = core::mem::replace(&mut self.nodes, IndexVec::new());
312 let old_root_ids = core::mem::take(&mut self.roots);
313 let (retained_nodes, id_remappings) = remove_nodes(old_nodes.into_inner(), nodes_to_remove);
314
315 self.remap_and_add_nodes(retained_nodes, &id_remappings);
316 self.remap_and_add_roots(old_root_ids, &id_remappings);
317
318 self.commitment = self.compute_mast_forest_commitment();
319
320 id_remappings
321 }
322
323 pub fn merge<'forest>(
370 forests: impl IntoIterator<Item = &'forest MastForest>,
371 ) -> Result<(MastForest, MastForestRootMap), MastForestError> {
372 MastForestMerger::merge(forests)
373 }
374}
375
376impl MastForest {
379 #[cfg(test)]
380 fn assert_nodes_to_remove_are_orphaned(&self, nodes_to_remove: &BTreeSet<MastNodeId>) {
381 for (node_idx, node) in self.nodes.iter().enumerate() {
382 let node_id = MastNodeId::new_unchecked(node_idx.try_into().expect("too many nodes"));
383 if nodes_to_remove.contains(&node_id) {
384 continue;
385 }
386
387 node.for_each_child(|child_id| {
388 assert!(
389 !nodes_to_remove.contains(&child_id),
390 "cannot remove node {child_id:?}; retained node {node_id:?} references it"
391 );
392 });
393 }
394 }
395
396 #[cfg(test)]
402 fn remap_and_add_nodes(
403 &mut self,
404 nodes_to_add: Vec<MastNode>,
405 id_remappings: &BTreeMap<MastNodeId, MastNodeId>,
406 ) {
407 assert!(self.nodes.is_empty());
408 let node_builders =
409 nodes_to_add.into_iter().map(|node| node.to_builder(self)).collect::<Vec<_>>();
410
411 for live_node_builder in node_builders {
414 let node = live_node_builder.remap_children(id_remappings).build_linked().unwrap();
415 self.nodes.push(node).unwrap();
416 }
417 }
418
419 #[cfg(test)]
424 fn remap_and_add_roots(
425 &mut self,
426 old_root_ids: Vec<MastNodeId>,
427 id_remappings: &BTreeMap<MastNodeId, MastNodeId>,
428 ) {
429 assert!(self.roots.is_empty());
430
431 for old_root_id in old_root_ids {
432 if let Some(new_root_id) = id_remappings.get(&old_root_id).copied() {
433 self.make_root(new_root_id);
434 }
435 }
436 }
437}
438
439#[cfg(test)]
442fn remove_nodes(
443 mast_nodes: Vec<MastNode>,
444 nodes_to_remove: &BTreeSet<MastNodeId>,
445) -> (Vec<MastNode>, BTreeMap<MastNodeId, MastNodeId>) {
446 assert!(mast_nodes.len() < u32::MAX as usize);
448
449 let mut retained_nodes = Vec::with_capacity(mast_nodes.len());
450 let mut id_remappings = BTreeMap::new();
451
452 for (old_node_index, old_node) in mast_nodes.into_iter().enumerate() {
453 let old_node_id: MastNodeId = MastNodeId(old_node_index as u32);
454
455 if !nodes_to_remove.contains(&old_node_id) {
456 let new_node_id: MastNodeId = MastNodeId(retained_nodes.len() as u32);
457 id_remappings.insert(old_node_id, new_node_id);
458
459 retained_nodes.push(old_node);
460 }
461 }
462
463 (retained_nodes, id_remappings)
464}
465
466fn empty_mast_forest_commitment() -> MastForestCommitment {
467 let interface_commitment = crate::chiplets::hasher::hash_words(&[]);
468 let dependency_commitment = crate::chiplets::hasher::hash_words(&[]);
469 let advice_commitment = AdviceMap::default().commitment();
470 MastForestCommitment::new(interface_commitment, dependency_commitment, advice_commitment)
471}
472
473fn compute_nodes_commitment(
474 nodes: &IndexVec<MastNodeId, MastNode>,
475 node_ids: &[MastNodeId],
476) -> Word {
477 let mut digests: Vec<Word> = node_ids.iter().map(|&id| nodes[id].digest()).collect();
478 digests.sort_unstable();
479 crate::chiplets::hasher::hash_words(&digests)
480}
481
482fn compute_dependency_commitment(nodes: &IndexVec<MastNodeId, MastNode>) -> Word {
483 let mut digests: Vec<Word> = nodes
484 .iter()
485 .filter(|node| node.is_external())
486 .map(MastNodeExt::digest)
487 .collect();
488 digests.sort_unstable();
489 crate::chiplets::hasher::hash_words(&digests)
490}
491
492impl MastForestCommitment {
493 fn new(
494 interface_commitment: Word,
495 dependency_commitment: Word,
496 advice_commitment: Word,
497 ) -> Self {
498 let commitment = crate::chiplets::hasher::hash_words(&[
499 interface_commitment,
500 dependency_commitment,
501 advice_commitment,
502 ]);
503 Self {
504 commitment,
505 interface_commitment,
506 dependency_commitment,
507 advice_commitment,
508 }
509 }
510}
511
512fn compute_mast_forest_commitment(
513 nodes: &IndexVec<MastNodeId, MastNode>,
514 roots: &[MastNodeId],
515 advice_map: &AdviceMap,
516) -> MastForestCommitment {
517 let interface_commitment = compute_nodes_commitment(nodes, roots);
518 let dependency_commitment = compute_dependency_commitment(nodes);
519 let advice_commitment = advice_map.commitment();
520 MastForestCommitment::new(interface_commitment, dependency_commitment, advice_commitment)
521}
522
523impl MastForest {
526 #[inline(always)]
531 pub fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
532 self.nodes.get(node_id)
533 }
534
535 #[inline(always)]
537 pub fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
538 self.roots.iter().find(|&&root_id| self[root_id].digest() == digest).copied()
539 }
540
541 pub fn is_procedure_root(&self, node_id: MastNodeId) -> bool {
543 self.roots.contains(&node_id)
544 }
545
546 pub fn is_procedure_root_with_exact_digest(&self, node_id: MastNodeId, digest: Word) -> bool {
552 self.is_procedure_root(node_id) && self[node_id].digest() == digest
553 }
554
555 pub fn procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
557 self.roots.iter().map(|&root_id| self[root_id].digest())
558 }
559
560 pub fn local_procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
564 self.roots.iter().filter_map(|&root_id| {
565 let node = &self[root_id];
566 if node.is_external() { None } else { Some(node.digest()) }
567 })
568 }
569
570 pub fn procedure_roots(&self) -> &[MastNodeId] {
572 &self.roots
573 }
574
575 pub fn num_procedures(&self) -> u32 {
577 self.roots
578 .len()
579 .try_into()
580 .expect("MAST forest contains more than 2^32 procedures.")
581 }
582
583 pub fn compute_nodes_commitment<'a>(
588 &self,
589 node_ids: impl IntoIterator<Item = &'a MastNodeId>,
590 ) -> Word {
591 let node_ids = node_ids.into_iter().copied().collect::<Vec<_>>();
592 compute_nodes_commitment(&self.nodes, &node_ids)
593 }
594
595 pub fn interface_commitment(&self) -> Word {
600 self.commitment.interface_commitment
601 }
602
603 pub fn dependency_commitment(&self) -> Word {
608 self.commitment.dependency_commitment
609 }
610
611 pub fn advice_commitment(&self) -> Word {
615 self.commitment.advice_commitment
616 }
617
618 fn compute_mast_forest_commitment(&self) -> MastForestCommitment {
619 compute_mast_forest_commitment(&self.nodes, &self.roots, &self.advice_map)
620 }
621
622 pub fn commitment(&self) -> Word {
626 self.commitment.commitment
627 }
628
629 pub fn num_nodes(&self) -> u32 {
631 self.nodes.len() as u32
632 }
633
634 pub fn nodes(&self) -> &[MastNode] {
636 self.nodes.as_slice()
637 }
638
639 pub fn advice_map(&self) -> &AdviceMap {
640 &self.advice_map
641 }
642
643 pub fn with_advice_map(mut self, advice_map: AdviceMap) -> Self {
645 self.advice_map.extend(advice_map);
646 self.commitment = self.compute_mast_forest_commitment();
647 self
648 }
649
650 pub fn write_hashless<W: ByteWriter>(&self, target: &mut W) {
661 serialization::write_hashless_into(self, target);
662 }
663}
664
665impl MastForest {
667 pub(in crate::mast) fn validate_dense_node_order(&self) -> Result<(), MastForestError> {
668 validate_dense_node_order(&self.nodes)
669 }
670
671 fn validate_basic_block_invariants(&self) -> Result<(), MastForestError> {
672 for (node_id_idx, node) in self.nodes.iter().enumerate() {
673 let node_id =
674 MastNodeId::new_unchecked(node_id_idx.try_into().expect("too many nodes"));
675 if let MastNode::Block(basic_block) = node {
676 basic_block.validate_batch_invariants().map_err(|error_msg| {
677 MastForestError::InvalidBatchPadding(node_id, error_msg)
678 })?;
679 }
680 }
681
682 Ok(())
683 }
684
685 pub fn validate(&self) -> Result<(), MastForestError> {
696 self.validate_basic_block_invariants()?;
697 Ok(())
698 }
699
700 fn validate_node_hashes(&self) -> Result<(), MastForestError> {
705 let computed_hashes = self.compute_node_hashes()?;
706 for (node_idx, (node, computed_digest)) in
707 self.nodes.iter().zip(computed_hashes).enumerate()
708 {
709 let expected_digest = node.digest();
710 if expected_digest != computed_digest {
711 return Err(MastForestError::HashMismatch {
712 node_id: MastNodeId::new_unchecked(node_idx as u32),
713 expected: expected_digest,
714 computed: computed_digest,
715 });
716 }
717 }
718
719 Ok(())
720 }
721
722 fn compute_node_hashes(&self) -> Result<Vec<Word>, MastForestError> {
731 use crate::chiplets::hasher;
732
733 fn check_no_forward_ref(
735 node_id: MastNodeId,
736 child_id: MastNodeId,
737 ) -> Result<(), MastForestError> {
738 if child_id.0 >= node_id.0 {
739 return Err(MastForestError::ForwardReference(node_id, child_id));
740 }
741 Ok(())
742 }
743
744 let mut computed_hashes = Vec::with_capacity(self.nodes.len());
745 for (node_idx, node) in self.nodes.iter().enumerate() {
746 let node_id = MastNodeId::new_unchecked(node_idx as u32);
747
748 let computed_digest = match node {
750 MastNode::Block(block) => node::hash_op_batches(block.op_batches()),
751 MastNode::Join(join) => {
752 let left_id = join.first();
753 let right_id = join.second();
754 check_no_forward_ref(node_id, left_id)?;
755 check_no_forward_ref(node_id, right_id)?;
756
757 let left_digest = computed_hashes[left_id.0 as usize];
758 let right_digest = computed_hashes[right_id.0 as usize];
759 hasher::merge_in_mast_domain(&[left_digest, right_digest], JoinNode::DOMAIN)
760 },
761 MastNode::Split(split) => {
762 let true_id = split.on_true();
763 let false_id = split.on_false();
764 check_no_forward_ref(node_id, true_id)?;
765 check_no_forward_ref(node_id, false_id)?;
766
767 let true_digest = computed_hashes[true_id.0 as usize];
768 let false_digest = computed_hashes[false_id.0 as usize];
769 hasher::merge_in_mast_domain(&[true_digest, false_digest], SplitNode::DOMAIN)
770 },
771 MastNode::Loop(loop_node) => {
772 let body_id = loop_node.body();
773 check_no_forward_ref(node_id, body_id)?;
774
775 let body_digest = computed_hashes[body_id.0 as usize];
776 hasher::merge_in_mast_domain(&[body_digest, Word::default()], LoopNode::DOMAIN)
777 },
778 MastNode::Call(call) => {
779 let callee_id = call.callee();
780 check_no_forward_ref(node_id, callee_id)?;
781
782 let callee_digest = computed_hashes[callee_id.0 as usize];
783 let domain = if call.is_syscall() {
784 CallNode::SYSCALL_DOMAIN
785 } else {
786 CallNode::CALL_DOMAIN
787 };
788 hasher::merge_in_mast_domain(&[callee_digest, Word::default()], domain)
789 },
790 MastNode::Dyn(dyn_node) => {
791 if dyn_node.is_dyncall() {
792 DynNode::DYNCALL_DEFAULT_DIGEST
793 } else {
794 DynNode::DYN_DEFAULT_DIGEST
795 }
796 },
797 MastNode::External(_) => {
798 node.digest()
800 },
801 };
802
803 computed_hashes.push(computed_digest);
804 }
805
806 Ok(computed_hashes)
807 }
808}
809
810impl Index<MastNodeId> for MastForest {
814 type Output = MastNode;
815
816 #[inline(always)]
817 fn index(&self, node_id: MastNodeId) -> &Self::Output {
818 &self.nodes[node_id]
819 }
820}
821
822pub trait ExecutableMastForest {
832 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode>;
835
836 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word>;
845
846 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId>;
848
849 fn advice_map(&self) -> &AdviceMap;
851}
852
853impl ExecutableMastForest for MastForest {
854 #[inline(always)]
855 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
856 MastForest::get_node_by_id(self, node_id)
857 }
858
859 #[inline(always)]
860 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word> {
861 MastForest::get_node_by_id(self, node_id).map(MastNodeExt::digest)
862 }
863
864 #[inline(always)]
865 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
866 MastForest::find_procedure_root(self, digest)
867 }
868
869 #[inline(always)]
870 fn advice_map(&self) -> &AdviceMap {
871 MastForest::advice_map(self)
872 }
873}
874
875impl<T> Index<MastNodeId> for Arc<T>
879where
880 T: Index<MastNodeId, Output = MastNode> + ?Sized,
881{
882 type Output = MastNode;
883
884 #[inline(always)]
885 fn index(&self, node_id: MastNodeId) -> &Self::Output {
886 &(**self)[node_id]
887 }
888}
889
890impl<T: ExecutableMastForest + ?Sized> ExecutableMastForest for Arc<T> {
891 #[inline(always)]
892 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
893 T::get_node_by_id(self, node_id)
894 }
895
896 #[inline(always)]
897 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word> {
898 T::get_digest_by_id(self, node_id)
899 }
900
901 #[inline(always)]
902 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
903 T::find_procedure_root(self, digest)
904 }
905
906 #[inline(always)]
907 fn advice_map(&self) -> &AdviceMap {
908 T::advice_map(self)
909 }
910}
911
912#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
922#[cfg_attr(
923 all(feature = "arbitrary", test),
924 miden_test_serialization_macros::serialization_test
925)]
926pub struct MastNodeId(u32);
927
928pub type Remapping = BTreeMap<MastNodeId, MastNodeId>;
930
931impl MastNodeId {
932 pub fn from_u32_safe(
937 value: u32,
938 mast_forest: &MastForest,
939 ) -> Result<Self, DeserializationError> {
940 Self::from_u32_with_node_count(value, mast_forest.nodes.len())
941 }
942
943 pub fn new_unchecked(value: u32) -> Self {
945 Self(value)
946 }
947
948 pub(super) fn from_u32_with_node_count(
962 id: u32,
963 node_count: usize,
964 ) -> Result<Self, DeserializationError> {
965 if (id as usize) < node_count {
966 Ok(Self(id))
967 } else {
968 Err(DeserializationError::InvalidValue(format!(
969 "Invalid deserialized MAST node ID '{id}', but {node_count} is the number of nodes in the forest",
970 )))
971 }
972 }
973
974 pub fn remap(&self, remapping: &Remapping) -> Self {
976 *remapping.get(self).unwrap_or(self)
977 }
978}
979
980impl From<u32> for MastNodeId {
981 fn from(value: u32) -> Self {
982 MastNodeId::new_unchecked(value)
983 }
984}
985
986impl Idx for MastNodeId {}
987
988impl From<MastNodeId> for u32 {
989 fn from(value: MastNodeId) -> Self {
990 value.0
991 }
992}
993
994impl Serializable for MastNodeId {
995 fn write_into<W: ByteWriter>(&self, target: &mut W) {
996 Serializable::write_into(&self.0, target);
997 }
998}
999
1000impl Deserializable for MastNodeId {
1001 fn read_from<R: crate::serde::ByteReader>(
1002 source: &mut R,
1003 ) -> Result<Self, DeserializationError> {
1004 Ok(Self(<u32 as Deserializable>::read_from(source)?))
1005 }
1006
1007 fn min_serialized_size() -> usize {
1008 <u32 as Deserializable>::min_serialized_size()
1009 }
1010}
1011
1012impl fmt::Display for MastNodeId {
1013 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1014 write!(f, "MastNodeId({})", self.0)
1015 }
1016}
1017
1018#[cfg(any(test, feature = "arbitrary"))]
1019impl Arbitrary for MastNodeId {
1020 type Parameters = ();
1021
1022 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1023 use proptest::prelude::*;
1024 any::<u32>().prop_map(MastNodeId).boxed()
1025 }
1026
1027 type Strategy = BoxedStrategy<Self>;
1028}
1029
1030pub struct SubtreeIterator<'a> {
1035 forest: &'a MastForest,
1036 discovered: Vec<MastNodeId>,
1037 unvisited: Vec<MastNodeId>,
1038}
1039impl<'a> SubtreeIterator<'a> {
1040 pub fn new(root: &MastNodeId, forest: &'a MastForest) -> Self {
1041 let discovered = vec![];
1042 let unvisited = vec![*root];
1043 SubtreeIterator { forest, discovered, unvisited }
1044 }
1045}
1046impl Iterator for SubtreeIterator<'_> {
1047 type Item = MastNodeId;
1048 fn next(&mut self) -> Option<MastNodeId> {
1049 while let Some(id) = self.unvisited.pop() {
1050 let node = &self.forest[id];
1051 if !node.has_children() {
1052 return Some(id);
1053 } else {
1054 self.discovered.push(id);
1055 node.append_children_to(&mut self.unvisited);
1056 }
1057 }
1058 self.discovered.pop()
1059 }
1060}
1061
1062pub fn error_code_from_msg(msg: impl AsRef<str>) -> Felt {
1065 hash_string_to_word(msg.as_ref())[0]
1067}
1068
1069#[derive(Debug, thiserror::Error, PartialEq, Eq)]
1074pub enum MastForestError {
1075 #[error("MAST forest node count exceeds the maximum of {} nodes", MastForest::MAX_NODES)]
1076 TooManyNodes,
1077 #[error("node id {0} is greater than or equal to forest length {1}")]
1078 NodeIdOverflow(MastNodeId, usize),
1079 #[error("basic block cannot be created from an empty list of operations")]
1080 EmptyBasicBlock,
1081 #[error("advice map key {0} already exists when merging forests")]
1082 AdviceMapKeyCollisionOnMerge(Word),
1083 #[error("digest is required for deserialization")]
1084 DigestRequiredForDeserialization,
1085 #[error("invalid batch in basic block node {0:?}: {1}")]
1086 InvalidBatchPadding(MastNodeId, String),
1087 #[error("invalid node order at {node_id:?}: {reason}")]
1088 InvalidNodeOrder { node_id: MastNodeId, reason: String },
1089 #[error(
1090 "node {0:?} references child {1:?} which comes after it in the forest (forward reference)"
1091 )]
1092 ForwardReference(MastNodeId, MastNodeId),
1093 #[error("hash mismatch for node {node_id:?}: expected {expected:?}, computed {computed:?}")]
1094 HashMismatch {
1095 node_id: MastNodeId,
1096 expected: Word,
1097 computed: Word,
1098 },
1099 #[error("deserialization failed: {0}")]
1100 Deserialization(DeserializationError),
1101}
1102
1103