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