miden_core/mast/node/
external.rs

1use alloc::{boxed::Box, vec::Vec};
2use core::fmt;
3
4use miden_crypto::{Felt, Word};
5use miden_formatting::{
6    hex::ToHex,
7    prettier::{Document, PrettyPrint, const_text, nl, text},
8};
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12use super::{MastNodeErrorContext, MastNodeExt};
13use crate::mast::{DecoratedOpLink, DecoratorId, MastForest, MastNodeId, Remapping};
14
15// EXTERNAL NODE
16// ================================================================================================
17
18/// Node for referencing procedures not present in a given [`MastForest`] (hence "external").
19///
20/// External nodes can be used to verify the integrity of a program's hash while keeping parts of
21/// the program secret. They also allow a program to refer to a well-known procedure that was not
22/// compiled with the program (e.g. a procedure in the standard library).
23///
24/// The hash of an external node is the hash of the procedure it represents, such that an external
25/// node can be swapped with the actual subtree that it represents without changing the MAST root.
26#[derive(Clone, Debug, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub struct ExternalNode {
29    digest: Word,
30    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Vec::is_empty"))]
31    before_enter: Vec<DecoratorId>,
32    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Vec::is_empty"))]
33    after_exit: Vec<DecoratorId>,
34}
35
36impl ExternalNode {
37    /// Returns a new [`ExternalNode`] instantiated with the specified procedure hash.
38    pub fn new(procedure_hash: Word) -> Self {
39        Self {
40            digest: procedure_hash,
41            before_enter: Vec::new(),
42            after_exit: Vec::new(),
43        }
44    }
45}
46
47impl MastNodeErrorContext for ExternalNode {
48    fn decorators(&self) -> impl Iterator<Item = DecoratedOpLink> {
49        self.before_enter.iter().chain(&self.after_exit).copied().enumerate()
50    }
51}
52
53// PRETTY PRINTING
54// ================================================================================================
55
56impl ExternalNode {
57    pub(super) fn to_display<'a>(&'a self, mast_forest: &'a MastForest) -> impl fmt::Display + 'a {
58        ExternalNodePrettyPrint { node: self, mast_forest }
59    }
60
61    pub(super) fn to_pretty_print<'a>(
62        &'a self,
63        mast_forest: &'a MastForest,
64    ) -> impl PrettyPrint + 'a {
65        ExternalNodePrettyPrint { node: self, mast_forest }
66    }
67}
68
69struct ExternalNodePrettyPrint<'a> {
70    node: &'a ExternalNode,
71    mast_forest: &'a MastForest,
72}
73
74impl ExternalNodePrettyPrint<'_> {
75    /// Concatenates the provided decorators in a single line. If the list of decorators is not
76    /// empty, prepends `prepend` and appends `append` to the decorator document.
77    fn concatenate_decorators(
78        &self,
79        decorator_ids: &[DecoratorId],
80        prepend: Document,
81        append: Document,
82    ) -> Document {
83        let decorators = decorator_ids
84            .iter()
85            .map(|&decorator_id| self.mast_forest[decorator_id].render())
86            .reduce(|acc, doc| acc + const_text(" ") + doc)
87            .unwrap_or_default();
88
89        if decorators.is_empty() {
90            decorators
91        } else {
92            prepend + decorators + append
93        }
94    }
95
96    fn single_line_pre_decorators(&self) -> Document {
97        self.concatenate_decorators(self.node.before_enter(), Document::Empty, const_text(" "))
98    }
99
100    fn single_line_post_decorators(&self) -> Document {
101        self.concatenate_decorators(self.node.after_exit(), const_text(" "), Document::Empty)
102    }
103
104    fn multi_line_pre_decorators(&self) -> Document {
105        self.concatenate_decorators(self.node.before_enter(), Document::Empty, nl())
106    }
107
108    fn multi_line_post_decorators(&self) -> Document {
109        self.concatenate_decorators(self.node.after_exit(), nl(), Document::Empty)
110    }
111}
112
113impl crate::prettier::PrettyPrint for ExternalNodePrettyPrint<'_> {
114    fn render(&self) -> crate::prettier::Document {
115        let external = const_text("external")
116            + const_text(".")
117            + text(self.node.digest.as_bytes().to_hex_with_prefix());
118
119        let single_line = self.single_line_pre_decorators()
120            + external.clone()
121            + self.single_line_post_decorators();
122        let multi_line =
123            self.multi_line_pre_decorators() + external + self.multi_line_post_decorators();
124
125        single_line | multi_line
126    }
127}
128
129impl fmt::Display for ExternalNodePrettyPrint<'_> {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        use crate::prettier::PrettyPrint;
132        self.pretty_print(f)
133    }
134}
135
136// MAST NODE TRAIT IMPLEMENTATION
137// ================================================================================================
138
139impl MastNodeExt for ExternalNode {
140    /// Returns the commitment to the MAST node referenced by this external node.
141    ///
142    /// The hash of an external node is the hash of the procedure it represents, such that an
143    /// external node can be swapped with the actual subtree that it represents without changing
144    /// the MAST root.
145    fn digest(&self) -> Word {
146        self.digest
147    }
148
149    /// Returns the decorators to be executed before this node is executed.
150    fn before_enter(&self) -> &[DecoratorId] {
151        &self.before_enter
152    }
153
154    /// Returns the decorators to be executed after this node is executed.
155    fn after_exit(&self) -> &[DecoratorId] {
156        &self.after_exit
157    }
158
159    /// Sets the list of decorators to be executed before this node.
160    fn append_before_enter(&mut self, decorator_ids: &[DecoratorId]) {
161        self.before_enter.extend_from_slice(decorator_ids);
162    }
163
164    /// Sets the list of decorators to be executed after this node.
165    fn append_after_exit(&mut self, decorator_ids: &[DecoratorId]) {
166        self.after_exit.extend_from_slice(decorator_ids);
167    }
168
169    /// Removes all decorators from this node.
170    fn remove_decorators(&mut self) {
171        self.before_enter.truncate(0);
172        self.after_exit.truncate(0);
173    }
174
175    fn to_display<'a>(&'a self, mast_forest: &'a MastForest) -> Box<dyn fmt::Display + 'a> {
176        Box::new(ExternalNode::to_display(self, mast_forest))
177    }
178
179    fn to_pretty_print<'a>(&'a self, mast_forest: &'a MastForest) -> Box<dyn PrettyPrint + 'a> {
180        Box::new(ExternalNode::to_pretty_print(self, mast_forest))
181    }
182
183    fn remap_children(&self, _remapping: &Remapping) -> Self {
184        self.clone()
185    }
186
187    fn has_children(&self) -> bool {
188        false
189    }
190
191    fn append_children_to(&self, _target: &mut Vec<MastNodeId>) {
192        // No children for external nodes
193    }
194
195    fn for_each_child<F>(&self, _f: F)
196    where
197        F: FnMut(MastNodeId),
198    {
199        // ExternalNode has no children
200    }
201
202    fn domain(&self) -> Felt {
203        panic!("Can't fetch domain for an `External` node.")
204    }
205}