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::{MastArtifact, 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> {
159 match &package.mast {
160 MastArtifact::Executable(executable) => {
164 let program = executable.as_ref().clone();
165
166 Ok(NoteScript::new(program))
167 },
168 MastArtifact::Library(library) => Ok(NoteScript::from_library(library))?,
169 }
170 }
171
172 pub fn root(&self) -> Word {
177 self.mast[self.entrypoint].digest()
178 }
179
180 pub fn mast(&self) -> Arc<MastForest> {
182 self.mast.clone()
183 }
184
185 pub fn entrypoint(&self) -> MastNodeId {
187 self.entrypoint
188 }
189
190 pub fn clear_debug_info(&mut self) {
195 let mut mast = self.mast.clone();
196 Arc::make_mut(&mut mast).clear_debug_info();
197 self.mast = mast;
198 }
199
200 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
206 if advice_map.is_empty() {
207 return self;
208 }
209
210 let mut mast = (*self.mast).clone();
211 mast.advice_map_mut().extend(advice_map);
212 Self {
213 mast: Arc::new(mast),
214 entrypoint: self.entrypoint,
215 }
216 }
217}
218
219impl From<&NoteScript> for Vec<Felt> {
223 fn from(script: &NoteScript) -> Self {
224 let mut bytes = script.mast.to_bytes();
225 let len = bytes.len();
226
227 let missing = if !len.is_multiple_of(4) { 4 - (len % 4) } else { 0 };
229 bytes.resize(bytes.len() + missing, 0);
230
231 let final_size = 2 + bytes.len();
232 let mut result = Vec::with_capacity(final_size);
233
234 result.push(Felt::from(u32::from(script.entrypoint)));
236 result.push(Felt::new(len as u64));
237
238 let mut encoded: &[u8] = &bytes;
240 while encoded.len() >= 4 {
241 let (data, rest) =
242 encoded.split_first_chunk::<4>().expect("The length has been checked");
243 let number = u32::from_le_bytes(*data);
244 result.push(Felt::new(number.into()));
245
246 encoded = rest;
247 }
248
249 result
250 }
251}
252
253impl From<NoteScript> for Vec<Felt> {
254 fn from(value: NoteScript) -> Self {
255 (&value).into()
256 }
257}
258
259impl AsRef<NoteScript> for NoteScript {
260 fn as_ref(&self) -> &NoteScript {
261 self
262 }
263}
264
265impl TryFrom<&[Felt]> for NoteScript {
269 type Error = DeserializationError;
270
271 fn try_from(elements: &[Felt]) -> Result<Self, Self::Error> {
272 if elements.len() < 2 {
273 return Err(DeserializationError::UnexpectedEOF);
274 }
275
276 let entrypoint: u32 = elements[0]
277 .as_canonical_u64()
278 .try_into()
279 .map_err(|err: TryFromIntError| DeserializationError::InvalidValue(err.to_string()))?;
280 let len = elements[1].as_canonical_u64();
281 let mut data = Vec::with_capacity(elements.len() * 4);
282
283 for &felt in &elements[2..] {
284 let element: u32 =
285 felt.as_canonical_u64().try_into().map_err(|err: TryFromIntError| {
286 DeserializationError::InvalidValue(err.to_string())
287 })?;
288 data.extend(element.to_le_bytes())
289 }
290 data.shrink_to(len as usize);
291
292 let mast = MastForest::read_from_bytes(&data)?;
294 let entrypoint = MastNodeId::from_u32_safe(entrypoint, &mast)?;
295 Ok(NoteScript::from_parts(Arc::new(mast), entrypoint))
296 }
297}
298
299impl TryFrom<Vec<Felt>> for NoteScript {
300 type Error = DeserializationError;
301
302 fn try_from(value: Vec<Felt>) -> Result<Self, Self::Error> {
303 value.as_slice().try_into()
304 }
305}
306
307impl Serializable for NoteScript {
311 fn write_into<W: ByteWriter>(&self, target: &mut W) {
312 self.mast.write_into(target);
313 target.write_u32(u32::from(self.entrypoint));
314 }
315
316 fn get_size_hint(&self) -> usize {
317 let mast_size = self.mast.to_bytes().len();
321 let u32_size = 0u32.get_size_hint();
322
323 mast_size + u32_size
324 }
325}
326
327impl Deserializable for NoteScript {
328 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
329 let mast = MastForest::read_from(source)?;
330 let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast)?;
331
332 Ok(Self::from_parts(Arc::new(mast), entrypoint))
333 }
334}
335
336impl PrettyPrint for NoteScript {
340 fn render(&self) -> miden_core::prettier::Document {
341 use miden_core::prettier::*;
342 let entrypoint = self.mast[self.entrypoint].to_pretty_print(&self.mast);
343
344 indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
345 }
346}
347
348impl Display for NoteScript {
349 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
350 self.pretty_print(f)
351 }
352}
353
354fn create_external_node_forest(digest: Word) -> (MastForest, MastNodeId) {
363 let mut mast = MastForest::new();
364 let node_id = ExternalNodeBuilder::new(digest)
365 .add_to_forest(&mut mast)
366 .expect("adding external node to empty forest should not fail");
367 mast.make_root(node_id);
368 (mast, node_id)
369}
370
371#[cfg(test)]
375mod tests {
376 use super::{Felt, NoteScript, Vec};
377 use crate::assembly::Assembler;
378 use crate::testing::note::DEFAULT_NOTE_CODE;
379
380 #[test]
381 fn test_note_script_to_from_felt() {
382 let assembler = Assembler::default();
383 let script_src = DEFAULT_NOTE_CODE;
384 let program = assembler.assemble_program(script_src).unwrap();
385 let note_script = NoteScript::new(program);
386
387 let encoded: Vec<Felt> = (¬e_script).into();
388 let decoded: NoteScript = encoded.try_into().unwrap();
389
390 assert_eq!(note_script, decoded);
391 }
392
393 #[test]
394 fn test_note_script_with_advice_map() {
395 use miden_core::advice::AdviceMap;
396
397 use crate::Word;
398
399 let assembler = Assembler::default();
400 let program = assembler.assemble_program("begin nop end").unwrap();
401 let script = NoteScript::new(program);
402
403 assert!(script.mast().advice_map().is_empty());
404
405 let original_root = script.root();
407 let script = script.with_advice_map(AdviceMap::default());
408 assert_eq!(original_root, script.root());
409
410 let key = Word::from([5u32, 6, 7, 8]);
412 let value = vec![Felt::new(100)];
413 let mut advice_map = AdviceMap::default();
414 advice_map.insert(key, value.clone());
415
416 let script = script.with_advice_map(advice_map);
417
418 let mast = script.mast();
419 let stored = mast.advice_map().get(&key).expect("entry should be present");
420 assert_eq!(stored.as_ref(), value.as_slice());
421 }
422}