miden_protocol/note/
script.rs1use alloc::sync::Arc;
2use alloc::vec::Vec;
3use core::fmt::Display;
4
5use miden_core::mast::MastNodeExt;
6use miden_crypto_derive::WordWrapper;
7use miden_mast_package::Package;
8use miden_processor::LoadedMastForest;
9
10use super::Felt;
11use crate::assembly::Path;
12use crate::assembly::mast::{MastForest, MastNodeId};
13use crate::crypto::utils::{bytes_to_elements_with_padding, padded_elements_to_bytes};
14use crate::errors::NoteError;
15use crate::script::MastForestScript;
16use crate::utils::serde::{
17 ByteReader,
18 ByteWriter,
19 Deserializable,
20 DeserializationError,
21 Serializable,
22};
23use crate::vm::AdviceMap;
24use crate::{PrettyPrint, Word};
25
26const NOTE_SCRIPT_ATTRIBUTE: &str = "note_script";
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, WordWrapper)]
34pub struct NoteScriptRoot(Word);
35
36impl From<NoteScriptRoot> for Word {
37 fn from(root: NoteScriptRoot) -> Self {
38 root.0
39 }
40}
41
42impl Display for NoteScriptRoot {
43 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44 Display::fmt(&self.0, f)
45 }
46}
47
48impl Serializable for NoteScriptRoot {
49 fn write_into<W: ByteWriter>(&self, target: &mut W) {
50 target.write(self.0);
51 }
52
53 fn get_size_hint(&self) -> usize {
54 self.0.get_size_hint()
55 }
56}
57
58impl Deserializable for NoteScriptRoot {
59 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
60 let word: Word = source.read()?;
61 Ok(Self::from_raw(word))
62 }
63}
64
65#[derive(Debug, Clone)]
73pub struct NoteScript(MastForestScript);
74
75impl NoteScript {
76 pub fn from_bytes(bytes: &[u8]) -> Result<Self, NoteError> {
84 Self::read_from_bytes(bytes).map_err(NoteError::NoteScriptDeserializationError)
85 }
86
87 pub fn from_parts(mast: Arc<MastForest>, entrypoint: MastNodeId) -> Result<Self, NoteError> {
92 MastForestScript::from_parts(mast, entrypoint)
93 .map_err(NoteError::MastForestScript)
94 .map(Self)
95 }
96
97 pub fn from_package(package: &Package) -> Result<Self, NoteError> {
109 let script = MastForestScript::from_package(package, NOTE_SCRIPT_ATTRIBUTE)
110 .map_err(NoteError::MastForestScript)?;
111 Ok(Self(script))
112 }
113
114 pub fn from_package_reference(package: &Package, path: &Path) -> Result<Self, NoteError> {
132 let script = MastForestScript::from_package_reference(package, path, NOTE_SCRIPT_ATTRIBUTE)
133 .map_err(NoteError::MastForestScript)?;
134 Ok(Self(script))
135 }
136
137 pub fn root(&self) -> NoteScriptRoot {
142 NoteScriptRoot::from_raw(self.0.digest())
143 }
144
145 pub fn mast(&self) -> Arc<MastForest> {
147 self.0.mast()
148 }
149
150 pub fn loaded_mast_forest(&self) -> LoadedMastForest {
152 self.0.loaded_mast_forest()
153 }
154
155 pub fn entrypoint(&self) -> MastNodeId {
157 self.0.entrypoint()
158 }
159
160 pub fn clear_debug_info(&mut self) {
162 self.0.clear_debug_info();
163 }
164
165 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
171 Self(self.0.with_advice_map(advice_map))
172 }
173
174 pub fn to_elements(&self) -> Vec<Felt> {
181 bytes_to_elements_with_padding(&self.to_bytes())
182 }
183
184 pub fn try_from_elements(elements: &[Felt]) -> Result<Self, DeserializationError> {
190 let bytes = padded_elements_to_bytes(elements).ok_or_else(|| {
191 DeserializationError::InvalidValue("encoded note script is not padded".into())
192 })?;
193
194 Self::read_from_bytes(&bytes)
195 }
196}
197
198impl PartialEq for NoteScript {
199 fn eq(&self, other: &Self) -> bool {
200 self.0 == other.0
201 }
202}
203
204impl Eq for NoteScript {}
205
206impl AsRef<NoteScript> for NoteScript {
207 fn as_ref(&self) -> &NoteScript {
208 self
209 }
210}
211
212impl Serializable for NoteScript {
216 fn write_into<W: ByteWriter>(&self, target: &mut W) {
217 self.0.write_into(target);
218 }
219
220 fn get_size_hint(&self) -> usize {
221 self.0.get_size_hint()
222 }
223}
224
225impl Deserializable for NoteScript {
226 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
227 Ok(Self(MastForestScript::read_from(source)?))
228 }
229}
230
231impl PrettyPrint for NoteScript {
235 fn render(&self) -> miden_core::prettier::Document {
236 use miden_core::prettier::*;
237 let mast = self.0.mast();
238 let entrypoint = mast[self.0.entrypoint()].to_pretty_print(&mast);
239
240 indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
241 }
242}
243
244impl Display for NoteScript {
245 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
246 self.pretty_print(f)
247 }
248}
249
250#[cfg(test)]
254mod tests {
255
256 use super::{Felt, NoteScript};
257 use crate::testing::assembler::assemble_test_package;
258 use crate::testing::note::DEFAULT_NOTE_SCRIPT;
259
260 #[test]
261 fn test_note_script_to_from_mast_elements() -> anyhow::Result<()> {
262 let package = assemble_test_package(
263 "test-note-script-roundtrip",
264 "test::note_roundtrip",
265 DEFAULT_NOTE_SCRIPT,
266 );
267 let note_script = NoteScript::from_package(&package)?;
268
269 let encoded = note_script.to_elements();
270 let decoded = NoteScript::try_from_elements(&encoded)?;
271 assert_eq!(note_script, decoded);
272
273 Ok(())
274 }
275
276 #[test]
277 fn test_note_script_preserves_package_debug_info() {
278 let package = assemble_test_package(
279 "test-note-script-debug-info",
280 "test::note_debug_info",
281 DEFAULT_NOTE_SCRIPT,
282 );
283 let note_script = NoteScript::from_package(&package).unwrap();
284
285 assert!(note_script.loaded_mast_forest().package_debug_info().unwrap().is_some());
286 }
287
288 #[test]
289 fn test_note_script_with_advice_map() {
290 use miden_core::advice::AdviceMap;
291
292 use crate::Word;
293
294 let package = assemble_test_package(
295 "test-note-script-with-advice-map",
296 "test::note_with_advice_map",
297 DEFAULT_NOTE_SCRIPT,
298 );
299 let script = NoteScript::from_package(&package).unwrap();
300
301 assert!(script.mast().advice_map().is_empty());
302
303 let original_root = script.root();
305 let script = script.with_advice_map(AdviceMap::default());
306 assert_eq!(original_root, script.root());
307
308 let key = Word::from([5u32, 6, 7, 8]);
310 let value = vec![Felt::new_unchecked(100)];
311 let mut advice_map = AdviceMap::default();
312 advice_map.insert(key, value.clone());
313
314 let script = script.with_advice_map(advice_map);
315
316 let mast = script.mast();
317 let stored = mast.advice_map().get(&key).expect("entry should be present");
318 assert_eq!(stored.as_ref(), value.as_slice());
319 }
320
321 #[test]
322 fn test_note_script_from_executable_package() {
323 use assert_matches::assert_matches;
324
325 use crate::assembly::Assembler;
326 use crate::errors::NoteError;
327 use crate::script::MastForestScriptError;
328
329 let package = Assembler::default()
332 .assemble_program("test-note-script-executable", "begin nop end")
333 .unwrap();
334 assert_matches!(
335 NoteScript::from_package(&package),
336 Err(NoteError::MastForestScript(MastForestScriptError::ExecutablePackage))
337 );
338 }
339}