miden_objects/note/
script.rs

1use 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// NOTE SCRIPT
18// ================================================================================================
19
20/// An executable program of a note.
21///
22/// A note's script represents a program which must be executed for a note to be consumed. As such
23/// it defines the rules and side effects of consuming a given note.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct NoteScript {
26    mast: Arc<MastForest>,
27    entrypoint: MastNodeId,
28}
29
30impl NoteScript {
31    // CONSTRUCTORS
32    // --------------------------------------------------------------------------------------------
33
34    /// Returns a new [NoteScript] instantiated from the provided program.
35    pub fn new(code: Program) -> Self {
36        Self {
37            entrypoint: code.entrypoint(),
38            mast: code.mast_forest().clone(),
39        }
40    }
41
42    /// Returns a new [NoteScript] compiled from the provided source code using the specified
43    /// assembler.
44    ///
45    /// # Errors
46    /// Returns an error if the compilation of the provided source code fails.
47    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    /// Returns a new [NoteScript] deserialized from the provided bytes.
55    ///
56    /// # Errors
57    /// Returns an error if note script deserialization fails.
58    pub fn from_bytes(bytes: &[u8]) -> Result<Self, NoteError> {
59        Self::read_from_bytes(bytes).map_err(NoteError::NoteScriptDeserializationError)
60    }
61
62    /// Returns a new [NoteScript] instantiated from the provided components.
63    ///
64    /// # Panics
65    /// Panics if the specified entrypoint is not in the provided MAST forest.
66    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    // PUBLIC ACCESSORS
72    // --------------------------------------------------------------------------------------------
73
74    /// Returns MAST root of this note script.
75    pub fn hash(&self) -> Digest {
76        self.mast[self.entrypoint].digest()
77    }
78
79    /// Returns a reference to the [MastForest] backing this note script.
80    pub fn mast(&self) -> Arc<MastForest> {
81        self.mast.clone()
82    }
83}
84
85// CONVERSIONS INTO NOTE SCRIPT
86// ================================================================================================
87
88impl 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        // Pad the data so that it can be encoded with u32
94        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        // Push the length, this is used to remove the padding later
101        result.push(Felt::from(script.entrypoint.as_u32()));
102        result.push(Felt::new(len as u64));
103
104        // A Felt can not represent all u64 values, so the data is encoded using u32.
105        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
125// CONVERSIONS FROM NOTE SCRIPT
126// ================================================================================================
127
128impl 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
160// SERIALIZATION
161// ================================================================================================
162
163impl 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
179// PRETTY-PRINTING
180// ================================================================================================
181
182impl 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// TESTS
198// ================================================================================================
199
200#[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> = (&note_script).into();
212        let decoded: NoteScript = encoded.try_into().unwrap();
213
214        assert_eq!(note_script, decoded);
215    }
216}