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#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct Program {
24 mast_forest: Arc<MastForest>,
25 entrypoint: MastNodeId,
27 kernel: Kernel,
28}
29
30impl Program {
32 pub fn new(mast_forest: Arc<MastForest>, entrypoint: MastNodeId) -> Self {
39 Self::with_kernel(mast_forest, entrypoint, Kernel::default())
40 }
41
42 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 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
70impl Program {
73 pub fn hash(&self) -> RpoDigest {
77 self.mast_forest[self.entrypoint].digest()
78 }
79
80 pub fn entrypoint(&self) -> MastNodeId {
82 self.entrypoint
83 }
84
85 pub fn mast_forest(&self) -> &Arc<MastForest> {
87 &self.mast_forest
88 }
89
90 pub fn kernel(&self) -> &Kernel {
92 &self.kernel
93 }
94
95 #[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 #[inline(always)]
106 pub fn find_procedure_root(&self, digest: RpoDigest) -> Option<MastNodeId> {
107 self.mast_forest.find_procedure_root(digest)
108 }
109
110 pub fn num_procedures(&self) -> u32 {
112 self.mast_forest.num_procedures()
113 }
114}
115
116#[cfg(feature = "std")]
119impl Program {
120 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 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 Ok(err) => unsafe { core::ptr::read(&*err) },
145 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
176impl 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#[derive(Debug, Clone, Default, PartialEq, Eq)]
207pub struct ProgramInfo {
208 program_hash: RpoDigest,
209 kernel: Kernel,
210}
211
212impl ProgramInfo {
213 pub const fn new(program_hash: RpoDigest, kernel: Kernel) -> Self {
215 Self { program_hash, kernel }
216 }
217
218 pub const fn program_hash(&self) -> &RpoDigest {
220 &self.program_hash
221 }
222
223 pub const fn kernel(&self) -> &Kernel {
225 &self.kernel
226 }
227
228 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
243impl 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
261impl 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 result.extend_from_slice(self.program_hash.as_elements());
271
272 for proc_hash in self.kernel.proc_hashes() {
274 result.extend_from_slice(proc_hash.as_elements());
275 }
276 result
277 }
278}