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#[cfg(feature = "serde")]
54use serde::{Deserialize, Serialize};
55
56#[cfg(feature = "serde")]
57use crate::serde::SliceReader;
58
59mod dense_order;
60mod node;
61#[cfg(any(test, feature = "arbitrary"))]
62pub use node::arbitrary;
63pub(crate) use node::collect_immediate_placements;
64pub use node::{
65 BasicBlockNode, BasicBlockNodeBuilder, CallNode, CallNodeBuilder, DynNode, DynNodeBuilder,
66 ExternalNode, ExternalNodeBuilder, JoinNode, JoinNodeBuilder, LoopNode, LoopNodeBuilder,
67 MastForestContributor, MastNode, MastNodeBuilder, MastNodeContext, MastNodeExt, OP_BATCH_SIZE,
68 OP_GROUP_SIZE, OpBatch, SplitNode, SplitNodeBuilder,
69};
70
71use crate::{
72 Felt, Word,
73 advice::AdviceMap,
74 crypto::hash::Poseidon2,
75 serde::{ByteWriter, Deserializable, DeserializationError, Serializable},
76 utils::{DenseIdMap, Idx, IndexVec, hash_string_to_word},
77};
78
79mod serialization;
80pub use serialization::{
81 AdviceMapView, AdviceValueView, MastForestReadMode, MastForestReadView, MastForestView,
82 MastForestWireView, MastNodeEntry, MastNodeInfo,
83};
84
85mod dense_builder;
86pub use dense_builder::DenseMastForestBuilder;
87
88mod untrusted;
89pub use untrusted::{UntrustedMastForest, UntrustedMastForestReadOptions};
90
91mod merger;
92pub(crate) use merger::MastForestMerger;
93pub use merger::MastForestRootMap;
94
95mod multi_forest_node_iterator;
96pub(crate) use multi_forest_node_iterator::*;
97
98mod node_builder_utils;
99pub use node_builder_utils::build_node_with_remapped_ids;
100
101mod sparse;
102pub use sparse::{MastForestId, SparseMastForest, SparseMastForestBuilder, VisitKind};
103
104#[cfg(test)]
105mod tests;
106
107#[derive(Clone, Debug, Default)]
127#[cfg_attr(
128 all(feature = "arbitrary", test),
129 miden_test_serde_macros::serde_test(binary_serde(true))
130)]
131pub struct MastForest {
132 nodes: IndexVec<MastNodeId, MastNode>,
134
135 roots: Vec<MastNodeId>,
137
138 advice_map: AdviceMap,
140
141 commitment: MastForestCommitment,
143}
144
145#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
156struct MastForestCommitment {
157 commitment: Word,
159
160 interface_commitment: Word,
162
163 dependency_commitment: Word,
165
166 advice_commitment: Word,
168}
169
170pub(crate) struct MastForestParts {
172 pub nodes: IndexVec<MastNodeId, MastNode>,
173 pub roots: Vec<MastNodeId>,
174 pub advice_map: AdviceMap,
175}
176
177impl MastForest {
180 pub fn new() -> Self {
182 Self {
183 nodes: IndexVec::new(),
184 roots: Vec::new(),
185 advice_map: AdviceMap::default(),
186 commitment: empty_mast_forest_commitment(),
187 }
188 }
189
190 #[doc(hidden)]
194 pub fn from_raw_parts(
195 nodes: IndexVec<MastNodeId, MastNode>,
196 roots: Vec<MastNodeId>,
197 advice_map: AdviceMap,
198 ) -> Result<Self, MastForestError> {
199 Self::from_parts(MastForestParts { nodes, roots, advice_map })
200 }
201
202 #[doc(hidden)]
208 pub fn from_raw_parts_with_id_map(
209 nodes: IndexVec<MastNodeId, MastNode>,
210 roots: Vec<MastNodeId>,
211 advice_map: AdviceMap,
212 ) -> Result<(Self, DenseIdMap<MastNodeId, MastNodeId>), MastForestError> {
213 Self::from_parts_with_id_map(MastForestParts { nodes, roots, advice_map })
214 }
215
216 pub(crate) fn from_parts(parts: MastForestParts) -> Result<Self, MastForestError> {
218 Self::from_parts_with_id_map(parts).map(|(forest, _remapping)| forest)
219 }
220
221 pub(crate) fn from_parts_with_id_map(
222 parts: MastForestParts,
223 ) -> Result<(Self, DenseIdMap<MastNodeId, MastNodeId>), MastForestError> {
224 validate_mast_forest_parts_bounds(&parts)?;
225 let (parts, id_remapping) = canonicalize_parts(parts)?;
226
227 let forest = Self {
228 commitment: compute_mast_forest_commitment(
229 &parts.nodes,
230 &parts.roots,
231 &parts.advice_map,
232 ),
233 nodes: parts.nodes,
234 roots: parts.roots,
235 advice_map: parts.advice_map,
236 };
237
238 forest.validate_dense_node_order()?;
239 forest.validate()?;
240 forest.validate_node_hashes()?;
241 Ok((forest, id_remapping))
242 }
243
244 pub(in crate::mast) fn from_trusted_deserialization_parts(
245 parts: MastForestParts,
246 ) -> Result<Self, MastForestError> {
247 validate_mast_forest_parts_bounds(&parts)?;
248 validate_dense_node_order(&parts.nodes)?;
251 Ok(Self {
252 commitment: compute_mast_forest_commitment(
253 &parts.nodes,
254 &parts.roots,
255 &parts.advice_map,
256 ),
257 nodes: parts.nodes,
258 roots: parts.roots,
259 advice_map: parts.advice_map,
260 })
261 }
262}
263
264impl PartialEq for MastForest {
267 fn eq(&self, other: &Self) -> bool {
268 self.nodes == other.nodes
269 && self.roots == other.roots
270 && self.advice_map == other.advice_map
271 }
272}
273
274impl Eq for MastForest {}
275
276impl MastForest {
279 const MAX_NODES: usize = (1 << 30) - 1;
281
282 #[cfg(any(test, feature = "arbitrary"))]
290 pub fn make_root(&mut self, new_root_id: MastNodeId) {
291 assert!(new_root_id.to_usize() < self.nodes.len());
292
293 if !self.roots.contains(&new_root_id) {
294 self.roots.push(new_root_id);
295 self.commitment = self.compute_mast_forest_commitment();
296 }
297 }
298
299 #[cfg(test)]
307 fn remove_nodes(
308 &mut self,
309 nodes_to_remove: &BTreeSet<MastNodeId>,
310 ) -> BTreeMap<MastNodeId, MastNodeId> {
311 if nodes_to_remove.is_empty() {
312 return BTreeMap::new();
313 }
314
315 self.assert_nodes_to_remove_are_orphaned(nodes_to_remove);
316
317 let old_nodes = core::mem::replace(&mut self.nodes, IndexVec::new());
318 let old_root_ids = core::mem::take(&mut self.roots);
319 let (retained_nodes, id_remappings) = remove_nodes(old_nodes.into_inner(), nodes_to_remove);
320
321 self.remap_and_add_nodes(retained_nodes, &id_remappings);
322 self.remap_and_add_roots(old_root_ids, &id_remappings);
323
324 self.commitment = self.compute_mast_forest_commitment();
325
326 id_remappings
327 }
328
329 pub fn merge<'forest>(
376 forests: impl IntoIterator<Item = &'forest MastForest>,
377 ) -> Result<(MastForest, MastForestRootMap), MastForestError> {
378 MastForestMerger::merge(forests)
379 }
380}
381
382impl MastForest {
385 #[cfg(test)]
386 fn assert_nodes_to_remove_are_orphaned(&self, nodes_to_remove: &BTreeSet<MastNodeId>) {
387 for (node_idx, node) in self.nodes.iter().enumerate() {
388 let node_id = MastNodeId::new_unchecked(node_idx.try_into().expect("too many nodes"));
389 if nodes_to_remove.contains(&node_id) {
390 continue;
391 }
392
393 node.for_each_child(|child_id| {
394 assert!(
395 !nodes_to_remove.contains(&child_id),
396 "cannot remove node {child_id:?}; retained node {node_id:?} references it"
397 );
398 });
399 }
400 }
401
402 #[cfg(test)]
408 fn remap_and_add_nodes(
409 &mut self,
410 nodes_to_add: Vec<MastNode>,
411 id_remappings: &BTreeMap<MastNodeId, MastNodeId>,
412 ) {
413 assert!(self.nodes.is_empty());
414 let node_builders =
415 nodes_to_add.into_iter().map(|node| node.to_builder(self)).collect::<Vec<_>>();
416
417 for live_node_builder in node_builders {
420 let node = live_node_builder.remap_children(id_remappings).build_linked().unwrap();
421 self.nodes.push(node).unwrap();
422 }
423 }
424
425 #[cfg(test)]
430 fn remap_and_add_roots(
431 &mut self,
432 old_root_ids: Vec<MastNodeId>,
433 id_remappings: &BTreeMap<MastNodeId, MastNodeId>,
434 ) {
435 assert!(self.roots.is_empty());
436
437 for old_root_id in old_root_ids {
438 if let Some(new_root_id) = id_remappings.get(&old_root_id).copied() {
439 self.make_root(new_root_id);
440 }
441 }
442 }
443}
444
445#[cfg(test)]
448fn remove_nodes(
449 mast_nodes: Vec<MastNode>,
450 nodes_to_remove: &BTreeSet<MastNodeId>,
451) -> (Vec<MastNode>, BTreeMap<MastNodeId, MastNodeId>) {
452 assert!(mast_nodes.len() < u32::MAX as usize);
454
455 let mut retained_nodes = Vec::with_capacity(mast_nodes.len());
456 let mut id_remappings = BTreeMap::new();
457
458 for (old_node_index, old_node) in mast_nodes.into_iter().enumerate() {
459 let old_node_id: MastNodeId = MastNodeId(old_node_index as u32);
460
461 if !nodes_to_remove.contains(&old_node_id) {
462 let new_node_id: MastNodeId = MastNodeId(retained_nodes.len() as u32);
463 id_remappings.insert(old_node_id, new_node_id);
464
465 retained_nodes.push(old_node);
466 }
467 }
468
469 (retained_nodes, id_remappings)
470}
471
472fn empty_mast_forest_commitment() -> MastForestCommitment {
473 let interface_commitment = Poseidon2::merge_many(&[]);
474 let dependency_commitment = Poseidon2::merge_many(&[]);
475 let advice_commitment = AdviceMap::default().commitment();
476 MastForestCommitment::new(interface_commitment, dependency_commitment, advice_commitment)
477}
478
479fn compute_nodes_commitment(
480 nodes: &IndexVec<MastNodeId, MastNode>,
481 node_ids: &[MastNodeId],
482) -> Word {
483 let mut digests: Vec<Word> = node_ids.iter().map(|&id| nodes[id].digest()).collect();
484 digests.sort_unstable();
485 Poseidon2::merge_many(&digests)
486}
487
488fn compute_dependency_commitment(nodes: &IndexVec<MastNodeId, MastNode>) -> Word {
489 let mut digests: Vec<Word> = nodes
490 .iter()
491 .filter(|node| node.is_external())
492 .map(MastNodeExt::digest)
493 .collect();
494 digests.sort_unstable();
495 Poseidon2::merge_many(&digests)
496}
497
498impl MastForestCommitment {
499 fn new(
500 interface_commitment: Word,
501 dependency_commitment: Word,
502 advice_commitment: Word,
503 ) -> Self {
504 let commitment = Poseidon2::merge_many(&[
505 interface_commitment,
506 dependency_commitment,
507 advice_commitment,
508 ]);
509 Self {
510 commitment,
511 interface_commitment,
512 dependency_commitment,
513 advice_commitment,
514 }
515 }
516}
517
518fn compute_mast_forest_commitment(
519 nodes: &IndexVec<MastNodeId, MastNode>,
520 roots: &[MastNodeId],
521 advice_map: &AdviceMap,
522) -> MastForestCommitment {
523 let interface_commitment = compute_nodes_commitment(nodes, roots);
524 let dependency_commitment = compute_dependency_commitment(nodes);
525 let advice_commitment = advice_map.commitment();
526 MastForestCommitment::new(interface_commitment, dependency_commitment, advice_commitment)
527}
528
529impl MastForest {
532 #[inline(always)]
537 pub fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
538 self.nodes.get(node_id)
539 }
540
541 #[inline(always)]
543 pub fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
544 self.roots.iter().find(|&&root_id| self[root_id].digest() == digest).copied()
545 }
546
547 pub fn is_procedure_root(&self, node_id: MastNodeId) -> bool {
549 self.roots.contains(&node_id)
550 }
551
552 pub fn is_procedure_root_with_exact_digest(&self, node_id: MastNodeId, digest: Word) -> bool {
558 self.is_procedure_root(node_id) && self[node_id].digest() == digest
559 }
560
561 pub fn procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
563 self.roots.iter().map(|&root_id| self[root_id].digest())
564 }
565
566 pub fn local_procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
570 self.roots.iter().filter_map(|&root_id| {
571 let node = &self[root_id];
572 if node.is_external() { None } else { Some(node.digest()) }
573 })
574 }
575
576 pub fn procedure_roots(&self) -> &[MastNodeId] {
578 &self.roots
579 }
580
581 pub fn num_procedures(&self) -> u32 {
583 self.roots
584 .len()
585 .try_into()
586 .expect("MAST forest contains more than 2^32 procedures.")
587 }
588
589 pub fn compute_nodes_commitment<'a>(
594 &self,
595 node_ids: impl IntoIterator<Item = &'a MastNodeId>,
596 ) -> Word {
597 let node_ids = node_ids.into_iter().copied().collect::<Vec<_>>();
598 compute_nodes_commitment(&self.nodes, &node_ids)
599 }
600
601 pub fn interface_commitment(&self) -> Word {
606 self.commitment.interface_commitment
607 }
608
609 pub fn dependency_commitment(&self) -> Word {
614 self.commitment.dependency_commitment
615 }
616
617 pub fn advice_commitment(&self) -> Word {
621 self.commitment.advice_commitment
622 }
623
624 fn compute_mast_forest_commitment(&self) -> MastForestCommitment {
625 compute_mast_forest_commitment(&self.nodes, &self.roots, &self.advice_map)
626 }
627
628 pub fn commitment(&self) -> Word {
632 self.commitment.commitment
633 }
634
635 pub fn num_nodes(&self) -> u32 {
637 self.nodes.len() as u32
638 }
639
640 pub fn nodes(&self) -> &[MastNode] {
642 self.nodes.as_slice()
643 }
644
645 pub fn advice_map(&self) -> &AdviceMap {
646 &self.advice_map
647 }
648
649 pub fn with_advice_map(mut self, advice_map: AdviceMap) -> Self {
651 self.advice_map.extend(advice_map);
652 self.commitment = self.compute_mast_forest_commitment();
653 self
654 }
655
656 pub fn write_hashless<W: ByteWriter>(&self, target: &mut W) {
667 serialization::write_hashless_into(self, target);
668 }
669}
670
671impl MastForest {
673 pub(in crate::mast) fn validate_dense_node_order(&self) -> Result<(), MastForestError> {
674 validate_dense_node_order(&self.nodes)
675 }
676
677 fn validate_basic_block_invariants(&self) -> Result<(), MastForestError> {
678 for (node_id_idx, node) in self.nodes.iter().enumerate() {
679 let node_id =
680 MastNodeId::new_unchecked(node_id_idx.try_into().expect("too many nodes"));
681 if let MastNode::Block(basic_block) = node {
682 basic_block.validate_batch_invariants().map_err(|error_msg| {
683 MastForestError::InvalidBatchPadding(node_id, error_msg)
684 })?;
685 }
686 }
687
688 Ok(())
689 }
690
691 pub fn validate(&self) -> Result<(), MastForestError> {
702 self.validate_basic_block_invariants()?;
703 Ok(())
704 }
705
706 fn validate_node_hashes(&self) -> Result<(), MastForestError> {
711 let computed_hashes = self.compute_node_hashes()?;
712 for (node_idx, (node, computed_digest)) in
713 self.nodes.iter().zip(computed_hashes).enumerate()
714 {
715 let expected_digest = node.digest();
716 if expected_digest != computed_digest {
717 return Err(MastForestError::HashMismatch {
718 node_id: MastNodeId::new_unchecked(node_idx as u32),
719 expected: expected_digest,
720 computed: computed_digest,
721 });
722 }
723 }
724
725 Ok(())
726 }
727
728 fn compute_node_hashes(&self) -> Result<Vec<Word>, MastForestError> {
737 use crate::chiplets::hasher;
738
739 fn check_no_forward_ref(
741 node_id: MastNodeId,
742 child_id: MastNodeId,
743 ) -> Result<(), MastForestError> {
744 if child_id.0 >= node_id.0 {
745 return Err(MastForestError::ForwardReference(node_id, child_id));
746 }
747 Ok(())
748 }
749
750 let mut computed_hashes = Vec::with_capacity(self.nodes.len());
751 for (node_idx, node) in self.nodes.iter().enumerate() {
752 let node_id = MastNodeId::new_unchecked(node_idx as u32);
753
754 let computed_digest = match node {
756 MastNode::Block(block) => {
757 let op_groups: Vec<Felt> =
758 block.op_batches().iter().flat_map(|batch| *batch.groups()).collect();
759 hasher::hash_elements(&op_groups)
760 },
761 MastNode::Join(join) => {
762 let left_id = join.first();
763 let right_id = join.second();
764 check_no_forward_ref(node_id, left_id)?;
765 check_no_forward_ref(node_id, right_id)?;
766
767 let left_digest = computed_hashes[left_id.0 as usize];
768 let right_digest = computed_hashes[right_id.0 as usize];
769 hasher::merge_in_domain(&[left_digest, right_digest], JoinNode::DOMAIN)
770 },
771 MastNode::Split(split) => {
772 let true_id = split.on_true();
773 let false_id = split.on_false();
774 check_no_forward_ref(node_id, true_id)?;
775 check_no_forward_ref(node_id, false_id)?;
776
777 let true_digest = computed_hashes[true_id.0 as usize];
778 let false_digest = computed_hashes[false_id.0 as usize];
779 hasher::merge_in_domain(&[true_digest, false_digest], SplitNode::DOMAIN)
780 },
781 MastNode::Loop(loop_node) => {
782 let body_id = loop_node.body();
783 check_no_forward_ref(node_id, body_id)?;
784
785 let body_digest = computed_hashes[body_id.0 as usize];
786 hasher::merge_in_domain(&[body_digest, Word::default()], LoopNode::DOMAIN)
787 },
788 MastNode::Call(call) => {
789 let callee_id = call.callee();
790 check_no_forward_ref(node_id, callee_id)?;
791
792 let callee_digest = computed_hashes[callee_id.0 as usize];
793 let domain = if call.is_syscall() {
794 CallNode::SYSCALL_DOMAIN
795 } else {
796 CallNode::CALL_DOMAIN
797 };
798 hasher::merge_in_domain(&[callee_digest, Word::default()], domain)
799 },
800 MastNode::Dyn(dyn_node) => {
801 if dyn_node.is_dyncall() {
802 DynNode::DYNCALL_DEFAULT_DIGEST
803 } else {
804 DynNode::DYN_DEFAULT_DIGEST
805 }
806 },
807 MastNode::External(_) => {
808 node.digest()
810 },
811 };
812
813 computed_hashes.push(computed_digest);
814 }
815
816 Ok(computed_hashes)
817 }
818}
819
820impl Index<MastNodeId> for MastForest {
824 type Output = MastNode;
825
826 #[inline(always)]
827 fn index(&self, node_id: MastNodeId) -> &Self::Output {
828 &self.nodes[node_id]
829 }
830}
831
832pub trait ExecutableMastForest {
842 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode>;
845
846 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word>;
855
856 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId>;
858
859 fn advice_map(&self) -> &AdviceMap;
861}
862
863impl ExecutableMastForest for MastForest {
864 #[inline(always)]
865 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
866 MastForest::get_node_by_id(self, node_id)
867 }
868
869 #[inline(always)]
870 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word> {
871 MastForest::get_node_by_id(self, node_id).map(MastNodeExt::digest)
872 }
873
874 #[inline(always)]
875 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
876 MastForest::find_procedure_root(self, digest)
877 }
878
879 #[inline(always)]
880 fn advice_map(&self) -> &AdviceMap {
881 MastForest::advice_map(self)
882 }
883}
884
885impl<T> Index<MastNodeId> for Arc<T>
889where
890 T: Index<MastNodeId, Output = MastNode> + ?Sized,
891{
892 type Output = MastNode;
893
894 #[inline(always)]
895 fn index(&self, node_id: MastNodeId) -> &Self::Output {
896 &(**self)[node_id]
897 }
898}
899
900impl<T: ExecutableMastForest + ?Sized> ExecutableMastForest for Arc<T> {
901 #[inline(always)]
902 fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
903 T::get_node_by_id(self, node_id)
904 }
905
906 #[inline(always)]
907 fn get_digest_by_id(&self, node_id: MastNodeId) -> Option<Word> {
908 T::get_digest_by_id(self, node_id)
909 }
910
911 #[inline(always)]
912 fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
913 T::find_procedure_root(self, digest)
914 }
915
916 #[inline(always)]
917 fn advice_map(&self) -> &AdviceMap {
918 T::advice_map(self)
919 }
920}
921
922#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
932#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
933#[cfg_attr(feature = "serde", serde(transparent))]
934pub struct MastNodeId(u32);
935
936pub type Remapping = BTreeMap<MastNodeId, MastNodeId>;
938
939impl MastNodeId {
940 pub fn from_u32_safe(
945 value: u32,
946 mast_forest: &MastForest,
947 ) -> Result<Self, DeserializationError> {
948 Self::from_u32_with_node_count(value, mast_forest.nodes.len())
949 }
950
951 pub fn new_unchecked(value: u32) -> Self {
953 Self(value)
954 }
955
956 pub(super) fn from_u32_with_node_count(
970 id: u32,
971 node_count: usize,
972 ) -> Result<Self, DeserializationError> {
973 if (id as usize) < node_count {
974 Ok(Self(id))
975 } else {
976 Err(DeserializationError::InvalidValue(format!(
977 "Invalid deserialized MAST node ID '{id}', but {node_count} is the number of nodes in the forest",
978 )))
979 }
980 }
981
982 pub fn remap(&self, remapping: &Remapping) -> Self {
984 *remapping.get(self).unwrap_or(self)
985 }
986}
987
988impl From<u32> for MastNodeId {
989 fn from(value: u32) -> Self {
990 MastNodeId::new_unchecked(value)
991 }
992}
993
994impl Idx for MastNodeId {}
995
996impl From<MastNodeId> for u32 {
997 fn from(value: MastNodeId) -> Self {
998 value.0
999 }
1000}
1001
1002impl Serializable for MastNodeId {
1003 fn write_into<W: ByteWriter>(&self, target: &mut W) {
1004 Serializable::write_into(&self.0, target);
1005 }
1006}
1007
1008impl Deserializable for MastNodeId {
1009 fn read_from<R: crate::serde::ByteReader>(
1010 source: &mut R,
1011 ) -> Result<Self, DeserializationError> {
1012 Ok(Self(<u32 as Deserializable>::read_from(source)?))
1013 }
1014
1015 fn min_serialized_size() -> usize {
1016 <u32 as Deserializable>::min_serialized_size()
1017 }
1018}
1019
1020impl fmt::Display for MastNodeId {
1021 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1022 write!(f, "MastNodeId({})", self.0)
1023 }
1024}
1025
1026#[cfg(any(test, feature = "arbitrary"))]
1027impl Arbitrary for MastNodeId {
1028 type Parameters = ();
1029
1030 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1031 use proptest::prelude::*;
1032 any::<u32>().prop_map(MastNodeId).boxed()
1033 }
1034
1035 type Strategy = BoxedStrategy<Self>;
1036}
1037
1038pub struct SubtreeIterator<'a> {
1043 forest: &'a MastForest,
1044 discovered: Vec<MastNodeId>,
1045 unvisited: Vec<MastNodeId>,
1046}
1047impl<'a> SubtreeIterator<'a> {
1048 pub fn new(root: &MastNodeId, forest: &'a MastForest) -> Self {
1049 let discovered = vec![];
1050 let unvisited = vec![*root];
1051 SubtreeIterator { forest, discovered, unvisited }
1052 }
1053}
1054impl Iterator for SubtreeIterator<'_> {
1055 type Item = MastNodeId;
1056 fn next(&mut self) -> Option<MastNodeId> {
1057 while let Some(id) = self.unvisited.pop() {
1058 let node = &self.forest[id];
1059 if !node.has_children() {
1060 return Some(id);
1061 } else {
1062 self.discovered.push(id);
1063 node.append_children_to(&mut self.unvisited);
1064 }
1065 }
1066 self.discovered.pop()
1067 }
1068}
1069
1070pub fn error_code_from_msg(msg: impl AsRef<str>) -> Felt {
1073 hash_string_to_word(msg.as_ref())[0]
1075}
1076
1077#[derive(Debug, thiserror::Error, PartialEq, Eq)]
1082pub enum MastForestError {
1083 #[error("MAST forest node count exceeds the maximum of {} nodes", MastForest::MAX_NODES)]
1084 TooManyNodes,
1085 #[error("node id {0} is greater than or equal to forest length {1}")]
1086 NodeIdOverflow(MastNodeId, usize),
1087 #[error("basic block cannot be created from an empty list of operations")]
1088 EmptyBasicBlock,
1089 #[error("advice map key {0} already exists when merging forests")]
1090 AdviceMapKeyCollisionOnMerge(Word),
1091 #[error("digest is required for deserialization")]
1092 DigestRequiredForDeserialization,
1093 #[error("invalid batch in basic block node {0:?}: {1}")]
1094 InvalidBatchPadding(MastNodeId, String),
1095 #[error("invalid node order at {node_id:?}: {reason}")]
1096 InvalidNodeOrder { node_id: MastNodeId, reason: String },
1097 #[error(
1098 "node {0:?} references child {1:?} which comes after it in the forest (forward reference)"
1099 )]
1100 ForwardReference(MastNodeId, MastNodeId),
1101 #[error("hash mismatch for node {node_id:?}: expected {expected:?}, computed {computed:?}")]
1102 HashMismatch {
1103 node_id: MastNodeId,
1104 expected: Word,
1105 computed: Word,
1106 },
1107 #[error("deserialization failed: {0}")]
1108 Deserialization(DeserializationError),
1109}
1110
1111#[cfg(feature = "serde")]
1113impl Serialize for MastForest {
1114 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1115 where
1116 S: serde::Serializer,
1117 {
1118 let bytes = Serializable::to_bytes(self);
1119 serializer.serialize_bytes(&bytes)
1120 }
1121}
1122
1123#[cfg(feature = "serde")]
1124impl<'de> Deserialize<'de> for MastForest {
1125 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1126 where
1127 D: serde::Deserializer<'de>,
1128 {
1129 let bytes = Vec::<u8>::deserialize(deserializer)?;
1131 let mut slice_reader = SliceReader::new(&bytes);
1132 Deserializable::read_from(&mut slice_reader).map_err(serde::de::Error::custom)
1133 }
1134}