1use alloc::{sync::Arc, vec::Vec};
2use core::fmt;
3
4use miden_crypto::{Felt, WORD_SIZE, Word};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7use winter_math::FieldElement;
8use winter_utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
9
10use super::Kernel;
11use crate::{
12 AdviceMap,
13 mast::{MastForest, MastNode, MastNodeExt, MastNodeId},
14 utils::ToElements,
15};
16
17#[derive(Clone, Debug, PartialEq, Eq)]
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27pub struct Program {
28 mast_forest: Arc<MastForest>,
29 entrypoint: MastNodeId,
31 kernel: Kernel,
32}
33
34impl Program {
36 pub fn new(mast_forest: Arc<MastForest>, entrypoint: MastNodeId) -> Self {
43 Self::with_kernel(mast_forest, entrypoint, Kernel::default())
44 }
45
46 pub fn with_kernel(
52 mast_forest: Arc<MastForest>,
53 entrypoint: MastNodeId,
54 kernel: Kernel,
55 ) -> Self {
56 assert!(mast_forest.get_node_by_id(entrypoint).is_some(), "invalid entrypoint");
57 assert!(mast_forest.is_procedure_root(entrypoint), "entrypoint not a procedure");
58
59 Self { mast_forest, entrypoint, kernel }
60 }
61
62 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
65 let mut mast_forest = (*self.mast_forest).clone();
66 mast_forest.advice_map_mut().extend(advice_map);
67 Self {
68 mast_forest: Arc::new(mast_forest),
69 ..self
70 }
71 }
72}
73
74impl Program {
77 pub fn hash(&self) -> Word {
81 self.mast_forest[self.entrypoint].digest()
82 }
83
84 pub fn entrypoint(&self) -> MastNodeId {
86 self.entrypoint
87 }
88
89 pub fn mast_forest(&self) -> &Arc<MastForest> {
91 &self.mast_forest
92 }
93
94 pub fn kernel(&self) -> &Kernel {
96 &self.kernel
97 }
98
99 #[inline(always)]
104 pub fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
105 self.mast_forest.get_node_by_id(node_id)
106 }
107
108 #[inline(always)]
110 pub fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
111 self.mast_forest.find_procedure_root(digest)
112 }
113
114 pub fn num_procedures(&self) -> u32 {
116 self.mast_forest.num_procedures()
117 }
118}
119
120#[cfg(feature = "std")]
123impl Program {
124 pub fn write_to_file<P>(&self, path: P) -> std::io::Result<()>
126 where
127 P: AsRef<std::path::Path>,
128 {
129 let path = path.as_ref();
130 if let Some(dir) = path.parent() {
131 std::fs::create_dir_all(dir)?;
132 }
133
134 std::panic::catch_unwind(|| match std::fs::File::create(path) {
139 Ok(ref mut file) => {
140 self.write_into(file);
141 Ok(())
142 },
143 Err(err) => Err(err),
144 })
145 .map_err(|p| {
146 match p.downcast::<std::io::Error>() {
147 Ok(err) => unsafe { core::ptr::read(&*err) },
149 Err(err) => std::panic::resume_unwind(err),
151 }
152 })?
153 }
154}
155
156impl Serializable for Program {
157 fn write_into<W: ByteWriter>(&self, target: &mut W) {
158 self.mast_forest.write_into(target);
159 self.kernel.write_into(target);
160 target.write_u32(self.entrypoint.as_u32());
161 }
162}
163
164impl Deserializable for Program {
165 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
166 let mast_forest = Arc::new(source.read()?);
167 let kernel = source.read()?;
168 let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast_forest)?;
169
170 if !mast_forest.is_procedure_root(entrypoint) {
171 return Err(DeserializationError::InvalidValue(format!(
172 "entrypoint {entrypoint} is not a procedure"
173 )));
174 }
175
176 Ok(Self::with_kernel(mast_forest, entrypoint, kernel))
177 }
178}
179
180impl crate::prettier::PrettyPrint for Program {
184 fn render(&self) -> crate::prettier::Document {
185 use crate::prettier::*;
186 let entrypoint = self.mast_forest[self.entrypoint()].to_pretty_print(&self.mast_forest);
187
188 indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
189 }
190}
191
192impl fmt::Display for Program {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 use crate::prettier::PrettyPrint;
195 self.pretty_print(f)
196 }
197}
198
199#[derive(Debug, Clone, Default, PartialEq, Eq)]
211pub struct ProgramInfo {
212 program_hash: Word,
213 kernel: Kernel,
214}
215
216impl ProgramInfo {
217 pub const fn new(program_hash: Word, kernel: Kernel) -> Self {
219 Self { program_hash, kernel }
220 }
221
222 pub const fn program_hash(&self) -> &Word {
224 &self.program_hash
225 }
226
227 pub const fn kernel(&self) -> &Kernel {
229 &self.kernel
230 }
231
232 pub fn kernel_procedures(&self) -> &[Word] {
234 self.kernel.proc_hashes()
235 }
236}
237
238impl From<Program> for ProgramInfo {
239 fn from(program: Program) -> Self {
240 let program_hash = program.hash();
241 let kernel = program.kernel().clone();
242
243 Self { program_hash, kernel }
244 }
245}
246
247impl Serializable for ProgramInfo {
251 fn write_into<W: ByteWriter>(&self, target: &mut W) {
252 self.program_hash.write_into(target);
253 self.kernel.write_into(target);
254 }
255}
256
257impl Deserializable for ProgramInfo {
258 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
259 let program_hash = source.read()?;
260 let kernel = source.read()?;
261 Ok(Self { program_hash, kernel })
262 }
263}
264
265impl ToElements for ProgramInfo {
269 fn to_elements(&self) -> Vec<Felt> {
270 let num_kernel_proc_elements = self.kernel.proc_hashes().len() * WORD_SIZE;
271 let mut result = Vec::with_capacity(2 * WORD_SIZE + num_kernel_proc_elements);
272
273 result.extend_from_slice(self.program_hash.as_elements());
277 result.extend_from_slice(&[Felt::ZERO; 4]);
278
279 for proc_hash in self.kernel.proc_hashes() {
283 let mut proc_hash_elements = proc_hash.as_elements().to_vec();
284 pad_next_mul_8(&mut proc_hash_elements);
285 proc_hash_elements.reverse();
286 result.extend_from_slice(&proc_hash_elements);
287 }
288 result
289 }
290}
291
292fn pad_next_mul_8(input: &mut Vec<Felt>) {
297 let output_len = input.len().next_multiple_of(8);
298 input.resize(output_len, Felt::ZERO);
299}