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))]
27#[cfg_attr(
28 all(feature = "arbitrary", test),
29 miden_test_serde_macros::serde_test(winter_serde(true))
30)]
31pub struct Program {
32 mast_forest: Arc<MastForest>,
33 entrypoint: MastNodeId,
35 kernel: Kernel,
36}
37
38impl Program {
40 pub fn new(mast_forest: Arc<MastForest>, entrypoint: MastNodeId) -> Self {
47 Self::with_kernel(mast_forest, entrypoint, Kernel::default())
48 }
49
50 pub fn with_kernel(
56 mast_forest: Arc<MastForest>,
57 entrypoint: MastNodeId,
58 kernel: Kernel,
59 ) -> Self {
60 assert!(mast_forest.get_node_by_id(entrypoint).is_some(), "invalid entrypoint");
61 assert!(mast_forest.is_procedure_root(entrypoint), "entrypoint not a procedure");
62
63 Self { mast_forest, entrypoint, kernel }
64 }
65
66 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
69 let mut mast_forest = (*self.mast_forest).clone();
70 mast_forest.advice_map_mut().extend(advice_map);
71 Self {
72 mast_forest: Arc::new(mast_forest),
73 ..self
74 }
75 }
76}
77
78impl Program {
81 pub fn hash(&self) -> Word {
85 self.mast_forest[self.entrypoint].digest()
86 }
87
88 pub fn entrypoint(&self) -> MastNodeId {
90 self.entrypoint
91 }
92
93 pub fn mast_forest(&self) -> &Arc<MastForest> {
95 &self.mast_forest
96 }
97
98 pub fn kernel(&self) -> &Kernel {
100 &self.kernel
101 }
102
103 #[inline(always)]
108 pub fn get_node_by_id(&self, node_id: MastNodeId) -> Option<&MastNode> {
109 self.mast_forest.get_node_by_id(node_id)
110 }
111
112 #[inline(always)]
114 pub fn find_procedure_root(&self, digest: Word) -> Option<MastNodeId> {
115 self.mast_forest.find_procedure_root(digest)
116 }
117
118 pub fn num_procedures(&self) -> u32 {
120 self.mast_forest.num_procedures()
121 }
122}
123
124#[cfg(feature = "std")]
127impl Program {
128 pub fn write_to_file<P>(&self, path: P) -> std::io::Result<()>
130 where
131 P: AsRef<std::path::Path>,
132 {
133 let path = path.as_ref();
134 if let Some(dir) = path.parent() {
135 std::fs::create_dir_all(dir)?;
136 }
137
138 std::panic::catch_unwind(|| match std::fs::File::create(path) {
143 Ok(ref mut file) => {
144 self.write_into(file);
145 Ok(())
146 },
147 Err(err) => Err(err),
148 })
149 .map_err(|p| {
150 match p.downcast::<std::io::Error>() {
151 Ok(err) => unsafe { core::ptr::read(&*err) },
153 Err(err) => std::panic::resume_unwind(err),
155 }
156 })?
157 }
158}
159
160impl Serializable for Program {
161 fn write_into<W: ByteWriter>(&self, target: &mut W) {
162 self.mast_forest.write_into(target);
163 self.kernel.write_into(target);
164 target.write_u32(self.entrypoint.into());
165 }
166}
167
168impl Deserializable for Program {
169 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
170 let mast_forest = Arc::new(source.read()?);
171 let kernel = source.read()?;
172 let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast_forest)?;
173
174 if !mast_forest.is_procedure_root(entrypoint) {
175 return Err(DeserializationError::InvalidValue(format!(
176 "entrypoint {entrypoint} is not a procedure"
177 )));
178 }
179
180 Ok(Self::with_kernel(mast_forest, entrypoint, kernel))
181 }
182}
183
184impl crate::prettier::PrettyPrint for Program {
188 fn render(&self) -> crate::prettier::Document {
189 use crate::prettier::*;
190 let entrypoint = self.mast_forest[self.entrypoint()].to_pretty_print(&self.mast_forest);
191
192 indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
193 }
194}
195
196impl fmt::Display for Program {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 use crate::prettier::PrettyPrint;
199 self.pretty_print(f)
200 }
201}
202
203#[derive(Debug, Clone, Default, PartialEq, Eq)]
215pub struct ProgramInfo {
216 program_hash: Word,
217 kernel: Kernel,
218}
219
220impl ProgramInfo {
221 pub const fn new(program_hash: Word, kernel: Kernel) -> Self {
223 Self { program_hash, kernel }
224 }
225
226 pub const fn program_hash(&self) -> &Word {
228 &self.program_hash
229 }
230
231 pub const fn kernel(&self) -> &Kernel {
233 &self.kernel
234 }
235
236 pub fn kernel_procedures(&self) -> &[Word] {
238 self.kernel.proc_hashes()
239 }
240}
241
242impl From<Program> for ProgramInfo {
243 fn from(program: Program) -> Self {
244 let program_hash = program.hash();
245 let kernel = program.kernel().clone();
246
247 Self { program_hash, kernel }
248 }
249}
250
251impl Serializable for ProgramInfo {
255 fn write_into<W: ByteWriter>(&self, target: &mut W) {
256 self.program_hash.write_into(target);
257 self.kernel.write_into(target);
258 }
259}
260
261impl Deserializable for ProgramInfo {
262 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
263 let program_hash = source.read()?;
264 let kernel = source.read()?;
265 Ok(Self { program_hash, kernel })
266 }
267}
268
269impl ToElements for ProgramInfo {
273 fn to_elements(&self) -> Vec<Felt> {
274 let num_kernel_proc_elements = self.kernel.proc_hashes().len() * WORD_SIZE;
275 let mut result = Vec::with_capacity(2 * WORD_SIZE + num_kernel_proc_elements);
276
277 result.extend_from_slice(self.program_hash.as_elements());
281 result.extend_from_slice(&[Felt::ZERO; 4]);
282
283 for proc_hash in self.kernel.proc_hashes() {
287 let mut proc_hash_elements = proc_hash.as_elements().to_vec();
288 pad_next_mul_8(&mut proc_hash_elements);
289 proc_hash_elements.reverse();
290 result.extend_from_slice(&proc_hash_elements);
291 }
292 result
293 }
294}
295
296fn pad_next_mul_8(input: &mut Vec<Felt>) {
301 let output_len = input.len().next_multiple_of(8);
302 input.resize(output_len, Felt::ZERO);
303}