miden_core/mast/node/
external.rs

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