miden_core/mast/node/
loop_node.rs

1use alloc::vec::Vec;
2use core::fmt;
3
4use miden_crypto::{Felt, hash::rpo::RpoDigest};
5use miden_formatting::prettier::PrettyPrint;
6
7use super::MastNodeExt;
8use crate::{
9    OPCODE_LOOP,
10    chiplets::hasher,
11    mast::{DecoratorId, MastForest, MastForestError, MastNodeId, Remapping},
12};
13
14// LOOP NODE
15// ================================================================================================
16
17/// A Loop node defines condition-controlled iterative execution. When the VM encounters a Loop
18/// node, it will keep executing the body of the loop as long as the top of the stack is `1``.
19///
20/// The loop is exited when at the end of executing the loop body the top of the stack is `0``.
21/// If the top of the stack is neither `0` nor `1` when the condition is checked, the execution
22/// fails.
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct LoopNode {
25    body: MastNodeId,
26    digest: RpoDigest,
27    before_enter: Vec<DecoratorId>,
28    after_exit: Vec<DecoratorId>,
29}
30
31/// Constants
32impl LoopNode {
33    /// The domain of the loop node (used for control block hashing).
34    pub const DOMAIN: Felt = Felt::new(OPCODE_LOOP as u64);
35}
36
37/// Constructors
38impl LoopNode {
39    /// Returns a new [`LoopNode`] instantiated with the specified body node.
40    pub fn new(body: MastNodeId, mast_forest: &MastForest) -> Result<Self, MastForestError> {
41        if body.as_usize() >= mast_forest.nodes.len() {
42            return Err(MastForestError::NodeIdOverflow(body, mast_forest.nodes.len()));
43        }
44        let digest = {
45            let body_hash = mast_forest[body].digest();
46
47            hasher::merge_in_domain(&[body_hash, RpoDigest::default()], Self::DOMAIN)
48        };
49
50        Ok(Self {
51            body,
52            digest,
53            before_enter: Vec::new(),
54            after_exit: Vec::new(),
55        })
56    }
57
58    /// Returns a new [`LoopNode`] from values that are assumed to be correct.
59    /// Should only be used when the source of the inputs is trusted (e.g. deserialization).
60    pub fn new_unsafe(body: MastNodeId, digest: RpoDigest) -> Self {
61        Self {
62            body,
63            digest,
64            before_enter: Vec::new(),
65            after_exit: Vec::new(),
66        }
67    }
68}
69
70impl LoopNode {
71    /// Returns a commitment to this Loop node.
72    ///
73    /// The commitment is computed as a hash of the loop body and an empty word ([ZERO; 4]) in
74    /// the domain defined by [Self::DOMAIN] - i..e,:
75    /// ```
76    /// # use miden_core::mast::LoopNode;
77    /// # use miden_crypto::{hash::rpo::{RpoDigest as Digest, Rpo256 as Hasher}};
78    /// # let body_digest = Digest::default();
79    /// Hasher::merge_in_domain(&[body_digest, Digest::default()], LoopNode::DOMAIN);
80    /// ```
81    pub fn digest(&self) -> RpoDigest {
82        self.digest
83    }
84
85    /// Returns the ID of the node presenting the body of the loop.
86    pub fn body(&self) -> MastNodeId {
87        self.body
88    }
89
90    /// Returns the decorators to be executed before this node is executed.
91    pub fn before_enter(&self) -> &[DecoratorId] {
92        &self.before_enter
93    }
94
95    /// Returns the decorators to be executed after this node is executed.
96    pub fn after_exit(&self) -> &[DecoratorId] {
97        &self.after_exit
98    }
99}
100
101/// Mutators
102impl LoopNode {
103    pub fn remap_children(&self, remapping: &Remapping) -> Self {
104        let mut node = self.clone();
105        node.body = node.body.remap(remapping);
106        node
107    }
108
109    /// Sets the list of decorators to be executed before this node.
110    pub fn append_before_enter(&mut self, decorator_ids: &[DecoratorId]) {
111        self.before_enter.extend_from_slice(decorator_ids);
112    }
113
114    /// Sets the list of decorators to be executed after this node.
115    pub fn append_after_exit(&mut self, decorator_ids: &[DecoratorId]) {
116        self.after_exit.extend_from_slice(decorator_ids);
117    }
118}
119
120impl MastNodeExt for LoopNode {
121    fn decorators(&self) -> impl Iterator<Item = (usize, DecoratorId)> {
122        self.before_enter.iter().chain(&self.after_exit).copied().enumerate()
123    }
124}
125
126// PRETTY PRINTING
127// ================================================================================================
128
129impl LoopNode {
130    pub(super) fn to_display<'a>(&'a self, mast_forest: &'a MastForest) -> impl fmt::Display + 'a {
131        LoopNodePrettyPrint { loop_node: self, mast_forest }
132    }
133
134    pub(super) fn to_pretty_print<'a>(
135        &'a self,
136        mast_forest: &'a MastForest,
137    ) -> impl PrettyPrint + 'a {
138        LoopNodePrettyPrint { loop_node: self, mast_forest }
139    }
140}
141
142struct LoopNodePrettyPrint<'a> {
143    loop_node: &'a LoopNode,
144    mast_forest: &'a MastForest,
145}
146
147impl crate::prettier::PrettyPrint for LoopNodePrettyPrint<'_> {
148    fn render(&self) -> crate::prettier::Document {
149        use crate::prettier::*;
150
151        let pre_decorators = {
152            let mut pre_decorators = self
153                .loop_node
154                .before_enter()
155                .iter()
156                .map(|&decorator_id| self.mast_forest[decorator_id].render())
157                .reduce(|acc, doc| acc + const_text(" ") + doc)
158                .unwrap_or_default();
159            if !pre_decorators.is_empty() {
160                pre_decorators += nl();
161            }
162
163            pre_decorators
164        };
165
166        let post_decorators = {
167            let mut post_decorators = self
168                .loop_node
169                .after_exit()
170                .iter()
171                .map(|&decorator_id| self.mast_forest[decorator_id].render())
172                .reduce(|acc, doc| acc + const_text(" ") + doc)
173                .unwrap_or_default();
174            if !post_decorators.is_empty() {
175                post_decorators = nl() + post_decorators;
176            }
177
178            post_decorators
179        };
180
181        let loop_body = self.mast_forest[self.loop_node.body].to_pretty_print(self.mast_forest);
182
183        pre_decorators
184            + indent(4, const_text("while.true") + nl() + loop_body.render())
185            + nl()
186            + const_text("end")
187            + post_decorators
188    }
189}
190
191impl fmt::Display for LoopNodePrettyPrint<'_> {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        use crate::prettier::PrettyPrint;
194        self.pretty_print(f)
195    }
196}