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 {
46 Self {
47 entrypoint: code.entrypoint(),
48 mast: code.mast_forest().clone(),
49 }
50 }
51
52 pub fn from_bytes(bytes: &[u8]) -> Result<Self, NoteError> {
57 Self::read_from_bytes(bytes).map_err(NoteError::NoteScriptDeserializationError)
58 }
59
60 pub fn from_parts(mast: Arc<MastForest>, entrypoint: MastNodeId) -> Self {
65 assert!(mast.get_node_by_id(entrypoint).is_some());
66 Self { mast, entrypoint }
67 }
68
69 pub fn from_library(library: &Library) -> Result<Self, NoteError> {
79 let mut entrypoint = None;
80
81 for export in library.exports() {
82 if let Some(proc_export) = export.as_procedure() {
83 if proc_export.attributes.has(NOTE_SCRIPT_ATTRIBUTE) {
85 if entrypoint.is_some() {
86 return Err(NoteError::NoteScriptMultipleProceduresWithAttribute);
87 }
88 entrypoint = Some(proc_export.node);
89 }
90 }
91 }
92
93 let entrypoint = entrypoint.ok_or(NoteError::NoteScriptNoProcedureWithAttribute)?;
94
95 Ok(Self {
96 mast: library.mast_forest().clone(),
97 entrypoint,
98 })
99 }
100
101 pub fn from_library_reference(library: &Library, path: &Path) -> Result<Self, NoteError> {
119 let export = library
121 .exports()
122 .find(|e| e.path().as_ref() == path)
123 .ok_or_else(|| NoteError::NoteScriptProcedureNotFound(path.to_string().into()))?;
124
125 let proc_export = export
127 .as_procedure()
128 .ok_or_else(|| NoteError::NoteScriptProcedureNotFound(path.to_string().into()))?;
129
130 if !proc_export.attributes.has(NOTE_SCRIPT_ATTRIBUTE) {
131 return Err(NoteError::NoteScriptProcedureMissingAttribute(path.to_string().into()));
132 }
133
134 let digest = library.mast_forest()[proc_export.node].digest();
136
137 let (mast, entrypoint) = create_external_node_forest(digest);
139
140 Ok(Self { mast: Arc::new(mast), entrypoint })
141 }
142
143 pub fn from_package(package: &Package) -> Result<Self, NoteError> {
153 Ok(NoteScript::from_library(&package.mast))?
154 }
155
156 pub fn root(&self) -> Word {
161 self.mast[self.entrypoint].digest()
162 }
163
164 pub fn mast(&self) -> Arc<MastForest> {
166 self.mast.clone()
167 }
168
169 pub fn entrypoint(&self) -> MastNodeId {
171 self.entrypoint
172 }
173
174 pub fn clear_debug_info(&mut self) {
179 let mut mast = self.mast.clone();
180 Arc::make_mut(&mut mast).clear_debug_info();
181 self.mast = mast;
182 }
183
184 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
190 if advice_map.is_empty() {
191 return self;
192 }
193
194 let mut mast = (*self.mast).clone();
195 mast.advice_map_mut().extend(advice_map);
196 Self {
197 mast: Arc::new(mast),
198 entrypoint: self.entrypoint,
199 }
200 }
201}
202
203impl From<&NoteScript> for Vec<Felt> {
207 fn from(script: &NoteScript) -> Self {
208 let mut bytes = script.mast.to_bytes();
209 let len = bytes.len();
210
211 let missing = if !len.is_multiple_of(4) { 4 - (len % 4) } else { 0 };
213 bytes.resize(bytes.len() + missing, 0);
214
215 let final_size = 2 + bytes.len();
216 let mut result = Vec::with_capacity(final_size);
217
218 result.push(Felt::from(u32::from(script.entrypoint)));
220 result.push(Felt::new(len as u64));
221
222 let mut encoded: &[u8] = &bytes;
224 while encoded.len() >= 4 {
225 let (data, rest) =
226 encoded.split_first_chunk::<4>().expect("The length has been checked");
227 let number = u32::from_le_bytes(*data);
228 result.push(Felt::new(number.into()));
229
230 encoded = rest;
231 }
232
233 result
234 }
235}
236
237impl From<NoteScript> for Vec<Felt> {
238 fn from(value: NoteScript) -> Self {
239 (&value).into()
240 }
241}
242
243impl AsRef<NoteScript> for NoteScript {
244 fn as_ref(&self) -> &NoteScript {
245 self
246 }
247}
248
249impl TryFrom<&[Felt]> for NoteScript {
253 type Error = DeserializationError;
254
255 fn try_from(elements: &[Felt]) -> Result<Self, Self::Error> {
256 if elements.len() < 2 {
257 return Err(DeserializationError::UnexpectedEOF);
258 }
259
260 let entrypoint: u32 = elements[0]
261 .as_canonical_u64()
262 .try_into()
263 .map_err(|err: TryFromIntError| DeserializationError::InvalidValue(err.to_string()))?;
264 let len = elements[1].as_canonical_u64();
265 let mut data = Vec::with_capacity(elements.len() * 4);
266
267 for &felt in &elements[2..] {
268 let element: u32 =
269 felt.as_canonical_u64().try_into().map_err(|err: TryFromIntError| {
270 DeserializationError::InvalidValue(err.to_string())
271 })?;
272 data.extend(element.to_le_bytes())
273 }
274 data.shrink_to(len as usize);
275
276 let mast = MastForest::read_from_bytes(&data)?;
278 let entrypoint = MastNodeId::from_u32_safe(entrypoint, &mast)?;
279 Ok(NoteScript::from_parts(Arc::new(mast), entrypoint))
280 }
281}
282
283impl TryFrom<Vec<Felt>> for NoteScript {
284 type Error = DeserializationError;
285
286 fn try_from(value: Vec<Felt>) -> Result<Self, Self::Error> {
287 value.as_slice().try_into()
288 }
289}
290
291impl Serializable for NoteScript {
295 fn write_into<W: ByteWriter>(&self, target: &mut W) {
296 self.mast.write_into(target);
297 target.write_u32(u32::from(self.entrypoint));
298 }
299
300 fn get_size_hint(&self) -> usize {
301 let mast_size = self.mast.to_bytes().len();
305 let u32_size = 0u32.get_size_hint();
306
307 mast_size + u32_size
308 }
309}
310
311impl Deserializable for NoteScript {
312 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
313 let mast = MastForest::read_from(source)?;
314 let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast)?;
315
316 Ok(Self::from_parts(Arc::new(mast), entrypoint))
317 }
318}
319
320impl PrettyPrint for NoteScript {
324 fn render(&self) -> miden_core::prettier::Document {
325 use miden_core::prettier::*;
326 let entrypoint = self.mast[self.entrypoint].to_pretty_print(&self.mast);
327
328 indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
329 }
330}
331
332impl Display for NoteScript {
333 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
334 self.pretty_print(f)
335 }
336}
337
338fn create_external_node_forest(digest: Word) -> (MastForest, MastNodeId) {
347 let mut mast = MastForest::new();
348 let node_id = ExternalNodeBuilder::new(digest)
349 .add_to_forest(&mut mast)
350 .expect("adding external node to empty forest should not fail");
351 mast.make_root(node_id);
352 (mast, node_id)
353}
354
355#[cfg(test)]
359mod tests {
360 use super::{Felt, NoteScript, Vec};
361 use crate::assembly::Assembler;
362 use crate::testing::note::DEFAULT_NOTE_CODE;
363
364 #[test]
365 fn test_note_script_to_from_felt() {
366 let assembler = Assembler::default();
367 let script_src = DEFAULT_NOTE_CODE;
368 let program = assembler.assemble_program(script_src).unwrap();
369 let note_script = NoteScript::new(program);
370
371 let encoded: Vec<Felt> = (¬e_script).into();
372 let decoded: NoteScript = encoded.try_into().unwrap();
373
374 assert_eq!(note_script, decoded);
375 }
376
377 #[test]
378 fn test_note_script_with_advice_map() {
379 use miden_core::advice::AdviceMap;
380
381 use crate::Word;
382
383 let assembler = Assembler::default();
384 let program = assembler.assemble_program("begin nop end").unwrap();
385 let script = NoteScript::new(program);
386
387 assert!(script.mast().advice_map().is_empty());
388
389 let original_root = script.root();
391 let script = script.with_advice_map(AdviceMap::default());
392 assert_eq!(original_root, script.root());
393
394 let key = Word::from([5u32, 6, 7, 8]);
396 let value = vec![Felt::new(100)];
397 let mut advice_map = AdviceMap::default();
398 advice_map.insert(key, value.clone());
399
400 let script = script.with_advice_map(advice_map);
401
402 let mast = script.mast();
403 let stored = mast.advice_map().get(&key).expect("entry should be present");
404 assert_eq!(stored.as_ref(), value.as_slice());
405 }
406}