miden_objects/note/
script.rs1use alloc::{sync::Arc, vec::Vec};
2use core::fmt::Display;
3
4use super::{Digest, Felt};
5use crate::{
6 NoteError, PrettyPrint,
7 assembly::{
8 Assembler, Compile,
9 mast::{MastForest, MastNodeId},
10 },
11 utils::serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
12 vm::Program,
13};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct NoteScript {
24 mast: Arc<MastForest>,
25 entrypoint: MastNodeId,
26}
27
28impl NoteScript {
29 pub fn new(code: Program) -> Self {
34 Self {
35 entrypoint: code.entrypoint(),
36 mast: code.mast_forest().clone(),
37 }
38 }
39
40 pub fn compile(source_code: impl Compile, assembler: Assembler) -> Result<Self, NoteError> {
46 let program = assembler
47 .assemble_program(source_code)
48 .map_err(NoteError::NoteScriptAssemblyError)?;
49 Ok(Self::new(program))
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 root(&self) -> Digest {
74 self.mast[self.entrypoint].digest()
75 }
76
77 pub fn mast(&self) -> Arc<MastForest> {
79 self.mast.clone()
80 }
81
82 pub fn entrypoint(&self) -> MastNodeId {
84 self.entrypoint
85 }
86}
87
88impl From<&NoteScript> for Vec<Felt> {
92 fn from(script: &NoteScript) -> Self {
93 let mut bytes = script.mast.to_bytes();
94 let len = bytes.len();
95
96 let missing = if len % 4 > 0 { 4 - (len % 4) } else { 0 };
98 bytes.resize(bytes.len() + missing, 0);
99
100 let final_size = 2 + bytes.len();
101 let mut result = Vec::with_capacity(final_size);
102
103 result.push(Felt::from(script.entrypoint.as_u32()));
105 result.push(Felt::new(len as u64));
106
107 let mut encoded: &[u8] = &bytes;
109 while encoded.len() >= 4 {
110 let (data, rest) =
111 encoded.split_first_chunk::<4>().expect("The length has been checked");
112 let number = u32::from_le_bytes(*data);
113 result.push(Felt::new(number.into()));
114
115 encoded = rest;
116 }
117
118 result
119 }
120}
121
122impl From<NoteScript> for Vec<Felt> {
123 fn from(value: NoteScript) -> Self {
124 (&value).into()
125 }
126}
127
128impl AsRef<NoteScript> for NoteScript {
129 fn as_ref(&self) -> &NoteScript {
130 self
131 }
132}
133
134impl TryFrom<&[Felt]> for NoteScript {
138 type Error = DeserializationError;
139
140 fn try_from(elements: &[Felt]) -> Result<Self, Self::Error> {
141 if elements.len() < 2 {
142 return Err(DeserializationError::UnexpectedEOF);
143 }
144
145 let entrypoint: u32 = elements[0].try_into().map_err(DeserializationError::InvalidValue)?;
146 let len = elements[1].as_int();
147 let mut data = Vec::with_capacity(elements.len() * 4);
148
149 for &felt in &elements[2..] {
150 let v: u32 = felt.try_into().map_err(DeserializationError::InvalidValue)?;
151 data.extend(v.to_le_bytes())
152 }
153 data.shrink_to(len as usize);
154
155 let mast = MastForest::read_from_bytes(&data)?;
156 let entrypoint = MastNodeId::from_u32_safe(entrypoint, &mast)?;
157 Ok(NoteScript::from_parts(Arc::new(mast), entrypoint))
158 }
159}
160
161impl TryFrom<Vec<Felt>> for NoteScript {
162 type Error = DeserializationError;
163
164 fn try_from(value: Vec<Felt>) -> Result<Self, Self::Error> {
165 value.as_slice().try_into()
166 }
167}
168
169impl Serializable for NoteScript {
173 fn write_into<W: ByteWriter>(&self, target: &mut W) {
174 self.mast.write_into(target);
175 target.write_u32(self.entrypoint.as_u32());
176 }
177}
178
179impl Deserializable for NoteScript {
180 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
181 let mast = MastForest::read_from(source)?;
182 let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast)?;
183
184 Ok(Self::from_parts(Arc::new(mast), entrypoint))
185 }
186}
187
188impl PrettyPrint for NoteScript {
192 fn render(&self) -> vm_core::prettier::Document {
193 use vm_core::prettier::*;
194 let entrypoint = self.mast[self.entrypoint].to_pretty_print(&self.mast);
195
196 indent(4, const_text("begin") + nl() + entrypoint.render()) + nl() + const_text("end")
197 }
198}
199
200impl Display for NoteScript {
201 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
202 self.pretty_print(f)
203 }
204}
205
206#[cfg(test)]
210mod tests {
211 use super::{Assembler, Felt, NoteScript, Vec};
212 use crate::testing::note::DEFAULT_NOTE_CODE;
213
214 #[test]
215 fn test_note_script_to_from_felt() {
216 let assembler = Assembler::default();
217 let tx_script_src = DEFAULT_NOTE_CODE;
218 let note_script = NoteScript::compile(tx_script_src, assembler).unwrap();
219
220 let encoded: Vec<Felt> = (¬e_script).into();
221 let decoded: NoteScript = encoded.try_into().unwrap();
222
223 assert_eq!(note_script, decoded);
224 }
225}