miden_objects/note/
script.rs1use alloc::{sync::Arc, vec::Vec};
2use core::fmt::Display;
3
4use assembly::{Assembler, Compile};
5use vm_core::{
6 mast::{MastForest, MastNodeId},
7 prettier::PrettyPrint,
8 Program,
9};
10
11use super::{Digest, Felt};
12use crate::{
13 utils::serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
14 NoteError,
15};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct NoteScript {
26 mast: Arc<MastForest>,
27 entrypoint: MastNodeId,
28}
29
30impl NoteScript {
31 pub fn new(code: Program) -> Self {
36 Self {
37 entrypoint: code.entrypoint(),
38 mast: code.mast_forest().clone(),
39 }
40 }
41
42 pub fn compile(source_code: impl Compile, assembler: Assembler) -> Result<Self, NoteError> {
48 let program = assembler
49 .assemble_program(source_code)
50 .map_err(NoteError::NoteScriptAssemblyError)?;
51 Ok(Self::new(program))
52 }
53
54 pub fn from_bytes(bytes: &[u8]) -> Result<Self, NoteError> {
59 Self::read_from_bytes(bytes).map_err(NoteError::NoteScriptDeserializationError)
60 }
61
62 pub fn from_parts(mast: Arc<MastForest>, entrypoint: MastNodeId) -> Self {
67 assert!(mast.get_node_by_id(entrypoint).is_some());
68 Self { mast, entrypoint }
69 }
70
71 pub fn hash(&self) -> Digest {
76 self.mast[self.entrypoint].digest()
77 }
78
79 pub fn mast(&self) -> Arc<MastForest> {
81 self.mast.clone()
82 }
83}
84
85impl From<&NoteScript> for Vec<Felt> {
89 fn from(script: &NoteScript) -> Self {
90 let mut bytes = script.mast.to_bytes();
91 let len = bytes.len();
92
93 let missing = if len % 4 > 0 { 4 - (len % 4) } else { 0 };
95 bytes.resize(bytes.len() + missing, 0);
96
97 let final_size = 2 + bytes.len();
98 let mut result = Vec::with_capacity(final_size);
99
100 result.push(Felt::from(script.entrypoint.as_u32()));
102 result.push(Felt::new(len as u64));
103
104 let mut encoded: &[u8] = &bytes;
106 while encoded.len() >= 4 {
107 let (data, rest) =
108 encoded.split_first_chunk::<4>().expect("The length has been checked");
109 let number = u32::from_le_bytes(*data);
110 result.push(Felt::new(number.into()));
111
112 encoded = rest;
113 }
114
115 result
116 }
117}
118
119impl From<NoteScript> for Vec<Felt> {
120 fn from(value: NoteScript) -> Self {
121 (&value).into()
122 }
123}
124
125impl TryFrom<&[Felt]> for NoteScript {
129 type Error = DeserializationError;
130
131 fn try_from(elements: &[Felt]) -> Result<Self, Self::Error> {
132 if elements.len() < 2 {
133 return Err(DeserializationError::UnexpectedEOF);
134 }
135
136 let entrypoint: u32 = elements[0].try_into().map_err(DeserializationError::InvalidValue)?;
137 let len = elements[1].as_int();
138 let mut data = Vec::with_capacity(elements.len() * 4);
139
140 for &felt in &elements[2..] {
141 let v: u32 = felt.try_into().map_err(DeserializationError::InvalidValue)?;
142 data.extend(v.to_le_bytes())
143 }
144 data.shrink_to(len as usize);
145
146 let mast = MastForest::read_from_bytes(&data)?;
147 let entrypoint = MastNodeId::from_u32_safe(entrypoint, &mast)?;
148 Ok(NoteScript::from_parts(Arc::new(mast), entrypoint))
149 }
150}
151
152impl TryFrom<Vec<Felt>> for NoteScript {
153 type Error = DeserializationError;
154
155 fn try_from(value: Vec<Felt>) -> Result<Self, Self::Error> {
156 value.as_slice().try_into()
157 }
158}
159
160impl Serializable for NoteScript {
164 fn write_into<W: ByteWriter>(&self, target: &mut W) {
165 self.mast.write_into(target);
166 target.write_u32(self.entrypoint.as_u32());
167 }
168}
169
170impl Deserializable for NoteScript {
171 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
172 let mast = MastForest::read_from(source)?;
173 let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast)?;
174
175 Ok(Self::from_parts(Arc::new(mast), entrypoint))
176 }
177}
178
179impl PrettyPrint for NoteScript {
183 fn render(&self) -> vm_core::prettier::Document {
184 use vm_core::prettier::*;
185 let entrypoint = self.mast[self.entrypoint].to_pretty_print(&self.mast);
186
187 indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
188 }
189}
190
191impl Display for NoteScript {
192 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
193 self.pretty_print(f)
194 }
195}
196
197#[cfg(test)]
201mod tests {
202 use super::{Assembler, Felt, NoteScript, Vec};
203 use crate::testing::note::DEFAULT_NOTE_CODE;
204
205 #[test]
206 fn test_note_script_to_from_felt() {
207 let assembler = Assembler::default();
208 let tx_script_src = DEFAULT_NOTE_CODE;
209 let note_script = NoteScript::compile(tx_script_src, assembler).unwrap();
210
211 let encoded: Vec<Felt> = (¬e_script).into();
212 let decoded: NoteScript = encoded.try_into().unwrap();
213
214 assert_eq!(note_script, decoded);
215 }
216}