miden_core/
program.rs

1use alloc::{sync::Arc, vec::Vec};
2use core::fmt;
3
4use miden_crypto::{Felt, WORD_SIZE, hash::rpo::RpoDigest};
5use winter_utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
6
7use super::Kernel;
8use crate::{
9    AdviceMap,
10    mast::{MastForest, MastNode, MastNodeId},
11    utils::ToElements,
12};
13
14// PROGRAM
15// ===============================================================================================
16
17/// An executable program for Miden VM.
18///
19/// A program consists of a MAST forest, an entrypoint defining the MAST node at which the program
20/// execution begins, and a definition of the kernel against which the program must be executed
21/// (the kernel can be an empty kernel).
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct Program {
24    mast_forest: Arc<MastForest>,
25    /// The "entrypoint" is the node where execution of the program begins.
26    entrypoint: MastNodeId,
27    kernel: Kernel,
28}
29
30/// Constructors
31impl Program {
32    /// Construct a new [`Program`] from the given MAST forest and entrypoint. The kernel is assumed
33    /// to be empty.
34    ///
35    /// # Panics:
36    /// - if `mast_forest` doesn't contain the specified entrypoint.
37    /// - if the specified entrypoint is not a procedure root in the `mast_forest`.
38    pub fn new(mast_forest: Arc<MastForest>, entrypoint: MastNodeId) -> Self {
39        Self::with_kernel(mast_forest, entrypoint, Kernel::default())
40    }
41
42    /// Construct a new [`Program`] from the given MAST forest, entrypoint, and kernel.
43    ///
44    /// # Panics:
45    /// - if `mast_forest` doesn't contain the specified entrypoint.
46    /// - if the specified entrypoint is not a procedure root in the `mast_forest`.
47    pub fn with_kernel(
48        mast_forest: Arc<MastForest>,
49        entrypoint: MastNodeId,
50        kernel: Kernel,
51    ) -> Self {
52        assert!(mast_forest.get_node_by_id(entrypoint).is_some(), "invalid entrypoint");
53        assert!(mast_forest.is_procedure_root(entrypoint), "entrypoint not a procedure");
54
55        Self { mast_forest, entrypoint, kernel }
56    }
57
58    /// Produces a new program with the existing [`MastForest`] and where all key/values in the
59    /// provided advice map are added to the internal advice map.
60    pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
61        let mut mast_forest = (*self.mast_forest).clone();
62        mast_forest.advice_map_mut().extend(advice_map);
63        Self {
64            mast_forest: Arc::new(mast_forest),
65            ..self
66        }
67    }
68}
69
70// ------------------------------------------------------------------------------------------------
71/// Public accessors
72impl Program {
73    /// Returns the hash of the program's entrypoint.
74    ///
75    /// Equivalently, returns the hash of the root of the entrypoint procedure.
76    pub fn hash(&self) -> RpoDigest {
77        self.mast_forest[self.entrypoint].digest()
78    }
79
80    /// Returns the entrypoint associated with this program.
81    pub fn entrypoint(&self) -> MastNodeId {
82        self.entrypoint
83    }
84
85    /// Returns a reference to the underlying [`MastForest`].
86    pub fn mast_forest(&self) -> &Arc<MastForest> {
87        &self.mast_forest
88    }
89
90    /// Returns the kernel associated with this program.
91    pub fn kernel(&self) -> &Kernel {
92        &self.kernel
93    }
94
95    /// Returns the [`MastNode`] associated with the provided [`MastNodeId`] if valid, or else
96    /// `None`.
97    ///
98    /// This is the fallible version of indexing (e.g. `program[node_id]`).
99    #[inline(always)]
100    pub fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
101        self.mast_forest.get_node_by_id(node_id)
102    }
103
104    /// Returns the [`MastNodeId`] of the procedure root associated with a given digest, if any.
105    #[inline(always)]
106    pub fn find_procedure_root(&self, digest: RpoDigest) -> Option<MastNodeId> {
107        self.mast_forest.find_procedure_root(digest)
108    }
109
110    /// Returns the number of procedures in this program.
111    pub fn num_procedures(&self) -> u32 {
112        self.mast_forest.num_procedures()
113    }
114}
115
116// ------------------------------------------------------------------------------------------------
117/// Serialization
118#[cfg(feature = "std")]
119impl Program {
120    /// Writes this [Program] to the provided file path.
121    pub fn write_to_file<P>(&self, path: P) -> std::io::Result<()>
122    where
123        P: AsRef<std::path::Path>,
124    {
125        let path = path.as_ref();
126        if let Some(dir) = path.parent() {
127            std::fs::create_dir_all(dir)?;
128        }
129
130        // NOTE: We're protecting against unwinds here due to i/o errors that will get turned into
131        // panics if writing to the underlying file fails. This is because ByteWriter does not have
132        // fallible APIs, thus WriteAdapter has to panic if writes fail. This could be fixed, but
133        // that has to happen upstream in winterfell
134        std::panic::catch_unwind(|| match std::fs::File::create(path) {
135            Ok(ref mut file) => {
136                self.write_into(file);
137                Ok(())
138            },
139            Err(err) => Err(err),
140        })
141        .map_err(|p| {
142            match p.downcast::<std::io::Error>() {
143                // SAFETY: It is guaranteed to be safe to read Box<std::io::Error>
144                Ok(err) => unsafe { core::ptr::read(&*err) },
145                // Propagate unknown panics
146                Err(err) => std::panic::resume_unwind(err),
147            }
148        })?
149    }
150}
151
152impl Serializable for Program {
153    fn write_into<W: ByteWriter>(&self, target: &mut W) {
154        self.mast_forest.write_into(target);
155        self.kernel.write_into(target);
156        target.write_u32(self.entrypoint.as_u32());
157    }
158}
159
160impl Deserializable for Program {
161    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
162        let mast_forest = Arc::new(source.read()?);
163        let kernel = source.read()?;
164        let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast_forest)?;
165
166        if !mast_forest.is_procedure_root(entrypoint) {
167            return Err(DeserializationError::InvalidValue(format!(
168                "entrypoint {entrypoint} is not a procedure"
169            )));
170        }
171
172        Ok(Self::with_kernel(mast_forest, entrypoint, kernel))
173    }
174}
175
176// ------------------------------------------------------------------------------------------------
177// Pretty-printing
178
179impl crate::prettier::PrettyPrint for Program {
180    fn render(&self) -> crate::prettier::Document {
181        use crate::prettier::*;
182        let entrypoint = self.mast_forest[self.entrypoint()].to_pretty_print(&self.mast_forest);
183
184        indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
185    }
186}
187
188impl fmt::Display for Program {
189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190        use crate::prettier::PrettyPrint;
191        self.pretty_print(f)
192    }
193}
194
195// PROGRAM INFO
196// ===============================================================================================
197
198/// A program information set consisting of its MAST root and set of kernel procedure roots used
199/// for its compilation.
200///
201/// This will be used as public inputs of the proof so we bind its verification to the kernel and
202/// root used to execute the program. This way, we extend the correctness of the proof to the
203/// security guarantees provided by the kernel. We also allow the user to easily prove the
204/// membership of a given kernel procedure for a given proof, without compromising its
205/// zero-knowledge properties.
206#[derive(Debug, Clone, Default, PartialEq, Eq)]
207pub struct ProgramInfo {
208    program_hash: RpoDigest,
209    kernel: Kernel,
210}
211
212impl ProgramInfo {
213    /// Creates a new instance of a program info.
214    pub const fn new(program_hash: RpoDigest, kernel: Kernel) -> Self {
215        Self { program_hash, kernel }
216    }
217
218    /// Returns the program hash computed from its code block root.
219    pub const fn program_hash(&self) -> &RpoDigest {
220        &self.program_hash
221    }
222
223    /// Returns the program kernel used during the compilation.
224    pub const fn kernel(&self) -> &Kernel {
225        &self.kernel
226    }
227
228    /// Returns the list of procedures of the kernel used during the compilation.
229    pub fn kernel_procedures(&self) -> &[RpoDigest] {
230        self.kernel.proc_hashes()
231    }
232}
233
234impl From<Program> for ProgramInfo {
235    fn from(program: Program) -> Self {
236        let program_hash = program.hash();
237        let kernel = program.kernel().clone();
238
239        Self { program_hash, kernel }
240    }
241}
242
243// ------------------------------------------------------------------------------------------------
244// Serialization
245
246impl Serializable for ProgramInfo {
247    fn write_into<W: ByteWriter>(&self, target: &mut W) {
248        self.program_hash.write_into(target);
249        self.kernel.write_into(target);
250    }
251}
252
253impl Deserializable for ProgramInfo {
254    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
255        let program_hash = source.read()?;
256        let kernel = source.read()?;
257        Ok(Self { program_hash, kernel })
258    }
259}
260
261// ------------------------------------------------------------------------------------------------
262// ToElements implementation
263
264impl ToElements for ProgramInfo {
265    fn to_elements(&self) -> Vec<Felt> {
266        let num_kernel_proc_elements = self.kernel.proc_hashes().len() * WORD_SIZE;
267        let mut result = Vec::with_capacity(WORD_SIZE + num_kernel_proc_elements);
268
269        // append program hash elements
270        result.extend_from_slice(self.program_hash.as_elements());
271
272        // append kernel procedure hash elements
273        for proc_hash in self.kernel.proc_hashes() {
274            result.extend_from_slice(proc_hash.as_elements());
275        }
276        result
277    }
278}