miden_protocol/note/
script.rs1use alloc::string::ToString;
2use alloc::sync::Arc;
3use alloc::vec::Vec;
4use core::fmt::Display;
5use core::num::TryFromIntError;
6
7use miden_core::mast::MastNodeExt;
8use miden_crypto_derive::WordWrapper;
9use miden_mast_package::Package;
10use miden_processor::LoadedMastForest;
11
12use super::Felt;
13use crate::assembly::Path;
14use crate::assembly::mast::{MastForest, MastNodeId};
15use crate::errors::NoteError;
16use crate::script::MastForestScript;
17use crate::utils::serde::{
18 ByteReader,
19 ByteWriter,
20 Deserializable,
21 DeserializationError,
22 Serializable,
23};
24use crate::vm::AdviceMap;
25use crate::{PrettyPrint, Word};
26
27const NOTE_SCRIPT_ATTRIBUTE: &str = "note_script";
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, WordWrapper)]
35pub struct NoteScriptRoot(Word);
36
37impl From<NoteScriptRoot> for Word {
38 fn from(root: NoteScriptRoot) -> Self {
39 root.0
40 }
41}
42
43impl Display for NoteScriptRoot {
44 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45 Display::fmt(&self.0, f)
46 }
47}
48
49impl Serializable for NoteScriptRoot {
50 fn write_into<W: ByteWriter>(&self, target: &mut W) {
51 target.write(self.0);
52 }
53
54 fn get_size_hint(&self) -> usize {
55 self.0.get_size_hint()
56 }
57}
58
59impl Deserializable for NoteScriptRoot {
60 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
61 let word: Word = source.read()?;
62 Ok(Self::from_raw(word))
63 }
64}
65
66#[derive(Debug, Clone)]
74pub struct NoteScript(MastForestScript);
75
76impl NoteScript {
77 pub fn from_bytes(bytes: &[u8]) -> Result<Self, NoteError> {
85 Self::read_from_bytes(bytes).map_err(NoteError::NoteScriptDeserializationError)
86 }
87
88 pub fn from_parts(mast: Arc<MastForest>, entrypoint: MastNodeId) -> Result<Self, NoteError> {
93 MastForestScript::from_parts(mast, entrypoint)
94 .map_err(NoteError::MastForestScript)
95 .map(Self)
96 }
97
98 pub fn from_package(package: &Package) -> Result<Self, NoteError> {
110 let script = MastForestScript::from_package(package, NOTE_SCRIPT_ATTRIBUTE)
111 .map_err(NoteError::MastForestScript)?;
112 Ok(Self(script))
113 }
114
115 pub fn from_package_reference(package: &Package, path: &Path) -> Result<Self, NoteError> {
133 let script = MastForestScript::from_package_reference(package, path, NOTE_SCRIPT_ATTRIBUTE)
134 .map_err(NoteError::MastForestScript)?;
135 Ok(Self(script))
136 }
137
138 pub fn root(&self) -> NoteScriptRoot {
143 NoteScriptRoot::from_raw(self.0.digest())
144 }
145
146 pub fn mast(&self) -> Arc<MastForest> {
148 self.0.mast()
149 }
150
151 pub fn loaded_mast_forest(&self) -> LoadedMastForest {
153 self.0.loaded_mast_forest()
154 }
155
156 pub fn entrypoint(&self) -> MastNodeId {
158 self.0.entrypoint()
159 }
160
161 pub fn clear_debug_info(&mut self) {
163 self.0.clear_debug_info();
164 }
165
166 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
172 Self(self.0.with_advice_map(advice_map))
173 }
174}
175
176impl PartialEq for NoteScript {
177 fn eq(&self, other: &Self) -> bool {
178 self.0 == other.0
179 }
180}
181
182impl Eq for NoteScript {}
183
184impl From<&NoteScript> for Vec<Felt> {
188 fn from(script: &NoteScript) -> Self {
189 let mut bytes = script.0.mast().to_bytes();
190 let len = bytes.len();
191
192 let missing = if !len.is_multiple_of(4) { 4 - (len % 4) } else { 0 };
194 bytes.resize(bytes.len() + missing, 0);
195
196 let final_size = 2 + bytes.len();
197 let mut result = Vec::with_capacity(final_size);
198
199 result.push(Felt::from(u32::from(script.0.entrypoint())));
201 result.push(Felt::new_unchecked(len as u64));
202
203 let mut encoded: &[u8] = &bytes;
205 while encoded.len() >= 4 {
206 let (data, rest) =
207 encoded.split_first_chunk::<4>().expect("The length has been checked");
208 let number = u32::from_le_bytes(*data);
209 result.push(Felt::from(number));
210
211 encoded = rest;
212 }
213
214 result
215 }
216}
217
218impl From<NoteScript> for Vec<Felt> {
219 fn from(value: NoteScript) -> Self {
220 (&value).into()
221 }
222}
223
224impl AsRef<NoteScript> for NoteScript {
225 fn as_ref(&self) -> &NoteScript {
226 self
227 }
228}
229
230impl TryFrom<&[Felt]> for NoteScript {
234 type Error = DeserializationError;
235
236 fn try_from(elements: &[Felt]) -> Result<Self, Self::Error> {
237 if elements.len() < 2 {
238 return Err(DeserializationError::UnexpectedEOF);
239 }
240
241 let entrypoint: u32 = elements[0]
242 .as_canonical_u64()
243 .try_into()
244 .map_err(|err: TryFromIntError| DeserializationError::InvalidValue(err.to_string()))?;
245 let len = elements[1].as_canonical_u64();
246 let mut data = Vec::with_capacity(elements.len() * 4);
247
248 for &felt in &elements[2..] {
249 let element: u32 =
250 felt.as_canonical_u64().try_into().map_err(|err: TryFromIntError| {
251 DeserializationError::InvalidValue(err.to_string())
252 })?;
253 data.extend(element.to_le_bytes())
254 }
255 data.truncate(len as usize);
256
257 let mast = MastForest::read_from_bytes(&data)?;
259 let entrypoint = MastNodeId::from_u32_safe(entrypoint, &mast)?;
260 NoteScript::from_parts(Arc::new(mast), entrypoint)
261 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
262 }
263}
264
265impl TryFrom<Vec<Felt>> for NoteScript {
266 type Error = DeserializationError;
267
268 fn try_from(value: Vec<Felt>) -> Result<Self, Self::Error> {
269 value.as_slice().try_into()
270 }
271}
272
273impl Serializable for NoteScript {
277 fn write_into<W: ByteWriter>(&self, target: &mut W) {
278 self.0.write_into(target);
279 }
280
281 fn get_size_hint(&self) -> usize {
282 self.0.get_size_hint()
283 }
284}
285
286impl Deserializable for NoteScript {
287 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
288 Ok(Self(MastForestScript::read_from(source)?))
289 }
290}
291
292impl PrettyPrint for NoteScript {
296 fn render(&self) -> miden_core::prettier::Document {
297 use miden_core::prettier::*;
298 let mast = self.0.mast();
299 let entrypoint = mast[self.0.entrypoint()].to_pretty_print(&mast);
300
301 indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
302 }
303}
304
305impl Display for NoteScript {
306 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
307 self.pretty_print(f)
308 }
309}
310
311#[cfg(test)]
315mod tests {
316
317 use super::{Felt, NoteScript, Vec};
318 use crate::testing::assembler::assemble_test_package;
319 use crate::testing::note::DEFAULT_NOTE_SCRIPT;
320
321 #[test]
322 fn test_note_script_to_from_felt() {
323 let script_src = DEFAULT_NOTE_SCRIPT;
324 let package =
325 assemble_test_package("test-note-script-roundtrip", "test::note_roundtrip", script_src);
326 let note_script = NoteScript::from_package(&package).unwrap();
327
328 let encoded: Vec<Felt> = (¬e_script).into();
329 let decoded: NoteScript = encoded.try_into().unwrap();
330
331 assert_eq!(note_script, decoded);
332 }
333
334 #[test]
335 fn test_note_script_preserves_package_debug_info() {
336 let package = assemble_test_package(
337 "test-note-script-debug-info",
338 "test::note_debug_info",
339 DEFAULT_NOTE_SCRIPT,
340 );
341 let note_script = NoteScript::from_package(&package).unwrap();
342
343 assert!(note_script.loaded_mast_forest().package_debug_info().unwrap().is_some());
344 }
345
346 #[test]
347 fn test_note_script_with_advice_map() {
348 use miden_core::advice::AdviceMap;
349
350 use crate::Word;
351
352 let package = assemble_test_package(
353 "test-note-script-with-advice-map",
354 "test::note_with_advice_map",
355 DEFAULT_NOTE_SCRIPT,
356 );
357 let script = NoteScript::from_package(&package).unwrap();
358
359 assert!(script.mast().advice_map().is_empty());
360
361 let original_root = script.root();
363 let script = script.with_advice_map(AdviceMap::default());
364 assert_eq!(original_root, script.root());
365
366 let key = Word::from([5u32, 6, 7, 8]);
368 let value = vec![Felt::new_unchecked(100)];
369 let mut advice_map = AdviceMap::default();
370 advice_map.insert(key, value.clone());
371
372 let script = script.with_advice_map(advice_map);
373
374 let mast = script.mast();
375 let stored = mast.advice_map().get(&key).expect("entry should be present");
376 assert_eq!(stored.as_ref(), value.as_slice());
377 }
378
379 #[test]
380 fn test_note_script_from_executable_package() {
381 use assert_matches::assert_matches;
382
383 use crate::assembly::Assembler;
384 use crate::errors::NoteError;
385 use crate::script::MastForestScriptError;
386
387 let package = Assembler::default()
390 .assemble_program("test-note-script-executable", "begin nop end")
391 .unwrap();
392 assert_matches!(
393 NoteScript::from_package(&package),
394 Err(NoteError::MastForestScript(MastForestScriptError::ExecutablePackage))
395 );
396 }
397}