miden_core/mast/node/
external.rs

1use alloc::vec::Vec;
2use core::fmt;
3
4use miden_crypto::hash::rpo::RpoDigest;
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: RpoDigest,
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: RpoDigest) -> 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) -> RpoDigest {
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/// Mutators
60impl ExternalNode {
61    /// Sets the list of decorators to be executed before this node.
62    pub fn append_before_enter(&mut self, decorator_ids: &[DecoratorId]) {
63        self.before_enter.extend_from_slice(decorator_ids);
64    }
65
66    /// Sets the list of decorators to be executed after this node.
67    pub fn append_after_exit(&mut self, decorator_ids: &[DecoratorId]) {
68        self.after_exit.extend_from_slice(decorator_ids);
69    }
70}
71
72impl MastNodeExt for ExternalNode {
73    fn decorators(&self) -> impl Iterator<Item = (usize, DecoratorId)> {
74        self.before_enter.iter().chain(&self.after_exit).copied().enumerate()
75    }
76}
77
78// PRETTY PRINTING
79// ================================================================================================
80
81impl ExternalNode {
82    pub(super) fn to_display<'a>(&'a self, mast_forest: &'a MastForest) -> impl fmt::Display + 'a {
83        ExternalNodePrettyPrint { node: self, mast_forest }
84    }
85
86    pub(super) fn to_pretty_print<'a>(
87        &'a self,
88        mast_forest: &'a MastForest,
89    ) -> impl PrettyPrint + 'a {
90        ExternalNodePrettyPrint { node: self, mast_forest }
91    }
92}
93
94struct ExternalNodePrettyPrint<'a> {
95    node: &'a ExternalNode,
96    mast_forest: &'a MastForest,
97}
98
99impl ExternalNodePrettyPrint<'_> {
100    /// Concatenates the provided decorators in a single line. If the list of decorators is not
101    /// empty, prepends `prepend` and appends `append` to the decorator document.
102    fn concatenate_decorators(
103        &self,
104        decorator_ids: &[DecoratorId],
105        prepend: Document,
106        append: Document,
107    ) -> Document {
108        let decorators = decorator_ids
109            .iter()
110            .map(|&decorator_id| self.mast_forest[decorator_id].render())
111            .reduce(|acc, doc| acc + const_text(" ") + doc)
112            .unwrap_or_default();
113
114        if decorators.is_empty() {
115            decorators
116        } else {
117            prepend + decorators + append
118        }
119    }
120
121    fn single_line_pre_decorators(&self) -> Document {
122        self.concatenate_decorators(self.node.before_enter(), Document::Empty, const_text(" "))
123    }
124
125    fn single_line_post_decorators(&self) -> Document {
126        self.concatenate_decorators(self.node.after_exit(), const_text(" "), Document::Empty)
127    }
128
129    fn multi_line_pre_decorators(&self) -> Document {
130        self.concatenate_decorators(self.node.before_enter(), Document::Empty, nl())
131    }
132
133    fn multi_line_post_decorators(&self) -> Document {
134        self.concatenate_decorators(self.node.after_exit(), nl(), Document::Empty)
135    }
136}
137
138impl crate::prettier::PrettyPrint for ExternalNodePrettyPrint<'_> {
139    fn render(&self) -> crate::prettier::Document {
140        let external = const_text("external")
141            + const_text(".")
142            + text(self.node.digest.as_bytes().to_hex_with_prefix());
143
144        let single_line = self.single_line_pre_decorators()
145            + external.clone()
146            + self.single_line_post_decorators();
147        let multi_line =
148            self.multi_line_pre_decorators() + external + self.multi_line_post_decorators();
149
150        single_line | multi_line
151    }
152}
153
154impl fmt::Display for ExternalNodePrettyPrint<'_> {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        use crate::prettier::PrettyPrint;
157        self.pretty_print(f)
158    }
159}