1use alloc::{sync::Arc, vec::Vec};
2use core::fmt;
3
4use miden_crypto::{hash::rpo::RpoDigest, Felt, WORD_SIZE};
5use winter_utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
6
7use super::Kernel;
8use crate::{
9 mast::{MastForest, MastNode, MastNodeId},
10 utils::ToElements,
11};
12
13#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct Program {
23 mast_forest: Arc<MastForest>,
24 entrypoint: MastNodeId,
26 kernel: Kernel,
27}
28
29impl Program {
31 pub fn new(mast_forest: Arc<MastForest>, entrypoint: MastNodeId) -> Self {
38 Self::with_kernel(mast_forest, entrypoint, Kernel::default())
39 }
40
41 pub fn with_kernel(
47 mast_forest: Arc<MastForest>,
48 entrypoint: MastNodeId,
49 kernel: Kernel,
50 ) -> Self {
51 assert!(mast_forest.get_node_by_id(entrypoint).is_some(), "invalid entrypoint");
52 assert!(mast_forest.is_procedure_root(entrypoint), "entrypoint not a procedure");
53
54 Self { mast_forest, entrypoint, kernel }
55 }
56}
57
58impl Program {
61 pub fn hash(&self) -> RpoDigest {
65 self.mast_forest[self.entrypoint].digest()
66 }
67
68 pub fn entrypoint(&self) -> MastNodeId {
70 self.entrypoint
71 }
72
73 pub fn mast_forest(&self) -> &Arc<MastForest> {
75 &self.mast_forest
76 }
77
78 pub fn kernel(&self) -> &Kernel {
80 &self.kernel
81 }
82
83 #[inline(always)]
88 pub fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
89 self.mast_forest.get_node_by_id(node_id)
90 }
91
92 #[inline(always)]
94 pub fn find_procedure_root(&self, digest: RpoDigest) -> Option<MastNodeId> {
95 self.mast_forest.find_procedure_root(digest)
96 }
97
98 pub fn num_procedures(&self) -> u32 {
100 self.mast_forest.num_procedures()
101 }
102}
103
104#[cfg(feature = "std")]
107impl Program {
108 pub fn write_to_file<P>(&self, path: P) -> std::io::Result<()>
110 where
111 P: AsRef<std::path::Path>,
112 {
113 let path = path.as_ref();
114 if let Some(dir) = path.parent() {
115 std::fs::create_dir_all(dir)?;
116 }
117
118 std::panic::catch_unwind(|| match std::fs::File::create(path) {
123 Ok(ref mut file) => {
124 self.write_into(file);
125 Ok(())
126 },
127 Err(err) => Err(err),
128 })
129 .map_err(|p| {
130 match p.downcast::<std::io::Error>() {
131 Ok(err) => unsafe { core::ptr::read(&*err) },
133 Err(err) => std::panic::resume_unwind(err),
135 }
136 })?
137 }
138}
139
140impl Serializable for Program {
141 fn write_into<W: ByteWriter>(&self, target: &mut W) {
142 self.mast_forest.write_into(target);
143 self.kernel.write_into(target);
144 target.write_u32(self.entrypoint.as_u32());
145 }
146}
147
148impl Deserializable for Program {
149 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
150 let mast_forest = Arc::new(source.read()?);
151 let kernel = source.read()?;
152 let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast_forest)?;
153
154 if !mast_forest.is_procedure_root(entrypoint) {
155 return Err(DeserializationError::InvalidValue(format!(
156 "entrypoint {entrypoint} is not a procedure"
157 )));
158 }
159
160 Ok(Self::with_kernel(mast_forest, entrypoint, kernel))
161 }
162}
163
164impl crate::prettier::PrettyPrint for Program {
168 fn render(&self) -> crate::prettier::Document {
169 use crate::prettier::*;
170 let entrypoint = self.mast_forest[self.entrypoint()].to_pretty_print(&self.mast_forest);
171
172 indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
173 }
174}
175
176impl fmt::Display for Program {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 use crate::prettier::PrettyPrint;
179 self.pretty_print(f)
180 }
181}
182
183#[derive(Debug, Clone, Default, PartialEq, Eq)]
195pub struct ProgramInfo {
196 program_hash: RpoDigest,
197 kernel: Kernel,
198}
199
200impl ProgramInfo {
201 pub const fn new(program_hash: RpoDigest, kernel: Kernel) -> Self {
203 Self { program_hash, kernel }
204 }
205
206 pub const fn program_hash(&self) -> &RpoDigest {
208 &self.program_hash
209 }
210
211 pub const fn kernel(&self) -> &Kernel {
213 &self.kernel
214 }
215
216 pub fn kernel_procedures(&self) -> &[RpoDigest] {
218 self.kernel.proc_hashes()
219 }
220}
221
222impl From<Program> for ProgramInfo {
223 fn from(program: Program) -> Self {
224 let program_hash = program.hash();
225 let kernel = program.kernel().clone();
226
227 Self { program_hash, kernel }
228 }
229}
230
231impl Serializable for ProgramInfo {
235 fn write_into<W: ByteWriter>(&self, target: &mut W) {
236 self.program_hash.write_into(target);
237 self.kernel.write_into(target);
238 }
239}
240
241impl Deserializable for ProgramInfo {
242 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
243 let program_hash = source.read()?;
244 let kernel = source.read()?;
245 Ok(Self { program_hash, kernel })
246 }
247}
248
249impl ToElements for ProgramInfo {
253 fn to_elements(&self) -> Vec<Felt> {
254 let num_kernel_proc_elements = self.kernel.proc_hashes().len() * WORD_SIZE;
255 let mut result = Vec::with_capacity(WORD_SIZE + num_kernel_proc_elements);
256
257 result.extend_from_slice(self.program_hash.as_elements());
259
260 for proc_hash in self.kernel.proc_hashes() {
262 result.extend_from_slice(proc_hash.as_elements());
263 }
264 result
265 }
266}