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_mast_package::Package;
9
10use super::Felt;
11use crate::assembly::mast::{ExternalNodeBuilder, MastForest, MastForestContributor, MastNodeId};
12use crate::assembly::{Library, Path};
13use crate::errors::NoteError;
14use crate::utils::serde::{
15 ByteReader,
16 ByteWriter,
17 Deserializable,
18 DeserializationError,
19 Serializable,
20};
21use crate::vm::{AdviceMap, Program};
22use crate::{PrettyPrint, Word};
23
24const NOTE_SCRIPT_ATTRIBUTE: &str = "note_script";
26
27#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct NoteScript {
36 mast: Arc<MastForest>,
37 entrypoint: MastNodeId,
38}
39
40impl NoteScript {
41 pub fn new(code: Program) -> Self {
50 Self {
51 entrypoint: code.entrypoint(),
52 mast: code.mast_forest().clone(),
53 }
54 }
55
56 pub fn from_bytes(bytes: &[u8]) -> Result<Self, NoteError> {
61 Self::read_from_bytes(bytes).map_err(NoteError::NoteScriptDeserializationError)
62 }
63
64 pub fn from_parts(mast: Arc<MastForest>, entrypoint: MastNodeId) -> Self {
69 assert!(mast.get_node_by_id(entrypoint).is_some());
70 Self { mast, entrypoint }
71 }
72
73 pub fn from_library(library: &Library) -> Result<Self, NoteError> {
83 let mut entrypoint = None;
84
85 for export in library.exports() {
86 if let Some(proc_export) = export.as_procedure() {
87 if proc_export.attributes.has(NOTE_SCRIPT_ATTRIBUTE) {
89 if entrypoint.is_some() {
90 return Err(NoteError::NoteScriptMultipleProceduresWithAttribute);
91 }
92 entrypoint = Some(proc_export.node);
93 }
94 }
95 }
96
97 let entrypoint = entrypoint.ok_or(NoteError::NoteScriptNoProcedureWithAttribute)?;
98
99 Ok(Self {
100 mast: library.mast_forest().clone(),
101 entrypoint,
102 })
103 }
104
105 pub fn from_library_reference(library: &Library, path: &Path) -> Result<Self, NoteError> {
123 let export = library
125 .exports()
126 .find(|e| e.path().as_ref() == path)
127 .ok_or_else(|| NoteError::NoteScriptProcedureNotFound(path.to_string().into()))?;
128
129 let proc_export = export
131 .as_procedure()
132 .ok_or_else(|| NoteError::NoteScriptProcedureNotFound(path.to_string().into()))?;
133
134 if !proc_export.attributes.has(NOTE_SCRIPT_ATTRIBUTE) {
135 return Err(NoteError::NoteScriptProcedureMissingAttribute(path.to_string().into()));
136 }
137
138 let digest = library.mast_forest()[proc_export.node].digest();
140
141 let (mast, entrypoint) = create_external_node_forest(digest);
143
144 Ok(Self { mast: Arc::new(mast), entrypoint })
145 }
146
147 pub fn from_package(package: &Package) -> Result<Self, NoteError> {
157 Ok(NoteScript::from_library(&package.mast))?
158 }
159
160 pub fn root(&self) -> Word {
165 self.mast[self.entrypoint].digest()
166 }
167
168 pub fn mast(&self) -> Arc<MastForest> {
170 self.mast.clone()
171 }
172
173 pub fn entrypoint(&self) -> MastNodeId {
175 self.entrypoint
176 }
177
178 pub fn clear_debug_info(&mut self) {
183 let mut mast = self.mast.clone();
184 Arc::make_mut(&mut mast).clear_debug_info();
185 self.mast = mast;
186 }
187
188 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
194 if advice_map.is_empty() {
195 return self;
196 }
197
198 let mut mast = (*self.mast).clone();
199 mast.advice_map_mut().extend(advice_map);
200 Self {
201 mast: Arc::new(mast),
202 entrypoint: self.entrypoint,
203 }
204 }
205}
206
207impl From<&NoteScript> for Vec<Felt> {
211 fn from(script: &NoteScript) -> Self {
212 let mut bytes = script.mast.to_bytes();
213 let len = bytes.len();
214
215 let missing = if !len.is_multiple_of(4) { 4 - (len % 4) } else { 0 };
217 bytes.resize(bytes.len() + missing, 0);
218
219 let final_size = 2 + bytes.len();
220 let mut result = Vec::with_capacity(final_size);
221
222 result.push(Felt::from(u32::from(script.entrypoint)));
224 result.push(Felt::new(len as u64));
225
226 let mut encoded: &[u8] = &bytes;
228 while encoded.len() >= 4 {
229 let (data, rest) =
230 encoded.split_first_chunk::<4>().expect("The length has been checked");
231 let number = u32::from_le_bytes(*data);
232 result.push(Felt::new(number.into()));
233
234 encoded = rest;
235 }
236
237 result
238 }
239}
240
241impl From<NoteScript> for Vec<Felt> {
242 fn from(value: NoteScript) -> Self {
243 (&value).into()
244 }
245}
246
247impl AsRef<NoteScript> for NoteScript {
248 fn as_ref(&self) -> &NoteScript {
249 self
250 }
251}
252
253impl TryFrom<&[Felt]> for NoteScript {
257 type Error = DeserializationError;
258
259 fn try_from(elements: &[Felt]) -> Result<Self, Self::Error> {
260 if elements.len() < 2 {
261 return Err(DeserializationError::UnexpectedEOF);
262 }
263
264 let entrypoint: u32 = elements[0]
265 .as_canonical_u64()
266 .try_into()
267 .map_err(|err: TryFromIntError| DeserializationError::InvalidValue(err.to_string()))?;
268 let len = elements[1].as_canonical_u64();
269 let mut data = Vec::with_capacity(elements.len() * 4);
270
271 for &felt in &elements[2..] {
272 let element: u32 =
273 felt.as_canonical_u64().try_into().map_err(|err: TryFromIntError| {
274 DeserializationError::InvalidValue(err.to_string())
275 })?;
276 data.extend(element.to_le_bytes())
277 }
278 data.shrink_to(len as usize);
279
280 let mast = MastForest::read_from_bytes(&data)?;
282 let entrypoint = MastNodeId::from_u32_safe(entrypoint, &mast)?;
283 Ok(NoteScript::from_parts(Arc::new(mast), entrypoint))
284 }
285}
286
287impl TryFrom<Vec<Felt>> for NoteScript {
288 type Error = DeserializationError;
289
290 fn try_from(value: Vec<Felt>) -> Result<Self, Self::Error> {
291 value.as_slice().try_into()
292 }
293}
294
295impl Serializable for NoteScript {
299 fn write_into<W: ByteWriter>(&self, target: &mut W) {
300 self.mast.write_into(target);
301 target.write_u32(u32::from(self.entrypoint));
302 }
303
304 fn get_size_hint(&self) -> usize {
305 let mast_size = self.mast.to_bytes().len();
309 let u32_size = 0u32.get_size_hint();
310
311 mast_size + u32_size
312 }
313}
314
315impl Deserializable for NoteScript {
316 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
317 let mast = MastForest::read_from(source)?;
318 let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast)?;
319
320 Ok(Self::from_parts(Arc::new(mast), entrypoint))
321 }
322}
323
324impl PrettyPrint for NoteScript {
328 fn render(&self) -> miden_core::prettier::Document {
329 use miden_core::prettier::*;
330 let entrypoint = self.mast[self.entrypoint].to_pretty_print(&self.mast);
331
332 indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
333 }
334}
335
336impl Display for NoteScript {
337 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
338 self.pretty_print(f)
339 }
340}
341
342fn create_external_node_forest(digest: Word) -> (MastForest, MastNodeId) {
351 let mut mast = MastForest::new();
352 let node_id = ExternalNodeBuilder::new(digest)
353 .add_to_forest(&mut mast)
354 .expect("adding external node to empty forest should not fail");
355 mast.make_root(node_id);
356 (mast, node_id)
357}
358
359#[cfg(test)]
363mod tests {
364 use super::{Felt, NoteScript, Vec};
365 use crate::assembly::Assembler;
366 use crate::testing::note::DEFAULT_NOTE_SCRIPT;
367
368 #[test]
369 fn test_note_script_to_from_felt() {
370 let assembler = Assembler::default();
371 let script_src = DEFAULT_NOTE_SCRIPT;
372 let library = assembler.assemble_library([script_src]).unwrap();
373 let note_script = NoteScript::from_library(&library).unwrap();
374
375 let encoded: Vec<Felt> = (¬e_script).into();
376 let decoded: NoteScript = encoded.try_into().unwrap();
377
378 assert_eq!(note_script, decoded);
379 }
380
381 #[test]
382 fn test_note_script_with_advice_map() {
383 use miden_core::advice::AdviceMap;
384
385 use crate::Word;
386
387 let assembler = Assembler::default();
388 let library = assembler.assemble_library([DEFAULT_NOTE_SCRIPT]).unwrap();
389 let script = NoteScript::from_library(&library).unwrap();
390
391 assert!(script.mast().advice_map().is_empty());
392
393 let original_root = script.root();
395 let script = script.with_advice_map(AdviceMap::default());
396 assert_eq!(original_root, script.root());
397
398 let key = Word::from([5u32, 6, 7, 8]);
400 let value = vec![Felt::new(100)];
401 let mut advice_map = AdviceMap::default();
402 advice_map.insert(key, value.clone());
403
404 let script = script.with_advice_map(advice_map);
405
406 let mast = script.mast();
407 let stored = mast.advice_map().get(&key).expect("entry should be present");
408 assert_eq!(stored.as_ref(), value.as_slice());
409 }
410}