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