miden_objects/account/code/
procedure.rs1use alloc::string::ToString;
2use alloc::sync::Arc;
3
4use miden_core::mast::MastForest;
5use miden_core::prettier::PrettyPrint;
6use miden_processor::{MastNode, MastNodeId};
7
8use super::Felt;
9use crate::utils::serde::{
10    ByteReader,
11    ByteWriter,
12    Deserializable,
13    DeserializationError,
14    Serializable,
15};
16use crate::{AccountError, FieldElement, Word};
17
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
37pub struct AccountProcedureInfo {
38    mast_root: Word,
39    storage_offset: u8,
40    storage_size: u8,
41}
42
43impl AccountProcedureInfo {
44    pub const NUM_ELEMENTS_PER_PROC: usize = 8;
46
47    pub fn new(
56        mast_root: Word,
57        storage_offset: u8,
58        storage_size: u8,
59    ) -> Result<Self, AccountError> {
60        if storage_size == 0 && storage_offset != 0 {
61            return Err(AccountError::PureProcedureWithStorageOffset);
62        }
63
64        if storage_offset.checked_add(storage_size).is_none() {
67            return Err(AccountError::StorageOffsetPlusSizeOutOfBounds(
68                storage_offset as u16 + storage_size as u16,
69            ));
70        }
71
72        Ok(Self { mast_root, storage_offset, storage_size })
73    }
74
75    pub fn mast_root(&self) -> &Word {
80        &self.mast_root
81    }
82
83    pub fn storage_offset(&self) -> u8 {
85        self.storage_offset
86    }
87
88    pub fn storage_size(&self) -> u8 {
90        self.storage_size
91    }
92}
93
94impl From<AccountProcedureInfo> for [Felt; 8] {
95    fn from(value: AccountProcedureInfo) -> Self {
96        let mut result = [Felt::ZERO; 8];
97
98        result[0..4].copy_from_slice(value.mast_root().as_elements());
100
101        result[4] = Felt::from(value.storage_offset);
103
104        result[5] = Felt::from(value.storage_size);
106
107        result
108    }
109}
110
111impl TryFrom<[Felt; 8]> for AccountProcedureInfo {
112    type Error = AccountError;
113
114    fn try_from(value: [Felt; 8]) -> Result<Self, Self::Error> {
115        let mast_root = Word::from(<[Felt; 4]>::try_from(&value[0..4]).unwrap());
117
118        let storage_offset: u8 = value[4].try_into().map_err(|_| {
120            AccountError::AccountCodeProcedureStorageOffsetTooLarge(mast_root, value[4])
121        })?;
122
123        let storage_size: u8 = value[5].try_into().map_err(|_| {
125            AccountError::AccountCodeProcedureStorageSizeTooLarge(mast_root, value[5])
126        })?;
127
128        if value[6] != Felt::ZERO || value[7] != Felt::ZERO {
130            return Err(AccountError::AccountCodeProcedureInvalidPadding(mast_root));
131        }
132
133        Ok(Self { mast_root, storage_offset, storage_size })
134    }
135}
136
137impl Serializable for AccountProcedureInfo {
138    fn write_into<W: ByteWriter>(&self, target: &mut W) {
139        target.write(self.mast_root);
140        target.write_u8(self.storage_offset);
141        target.write_u8(self.storage_size)
142    }
143
144    fn get_size_hint(&self) -> usize {
145        self.mast_root.get_size_hint()
146            + self.storage_offset.get_size_hint()
147            + self.storage_size.get_size_hint()
148    }
149}
150
151impl Deserializable for AccountProcedureInfo {
152    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
153        let mast_root: Word = source.read()?;
154        let storage_offset = source.read_u8()?;
155        let storage_size = source.read_u8()?;
156        Self::new(mast_root, storage_offset, storage_size)
157            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
158    }
159}
160
161#[derive(Debug, Clone)]
166pub struct PrintableProcedure {
167    mast: Arc<MastForest>,
168    procedure_info: AccountProcedureInfo,
169    entrypoint: MastNodeId,
170}
171
172impl PrintableProcedure {
173    pub(crate) fn new(
175        mast: Arc<MastForest>,
176        procedure_info: AccountProcedureInfo,
177        entrypoint: MastNodeId,
178    ) -> Self {
179        Self { mast, procedure_info, entrypoint }
180    }
181
182    fn entrypoint(&self) -> &MastNode {
183        &self.mast[self.entrypoint]
184    }
185
186    pub(crate) fn storage_offset(&self) -> u8 {
187        self.procedure_info.storage_offset()
188    }
189
190    pub(crate) fn storage_size(&self) -> u8 {
191        self.procedure_info.storage_size()
192    }
193
194    pub(crate) fn mast_root(&self) -> &Word {
195        self.procedure_info.mast_root()
196    }
197}
198
199impl PrettyPrint for PrintableProcedure {
200    fn render(&self) -> miden_core::prettier::Document {
201        use miden_core::prettier::*;
202
203        indent(
204            4,
205            const_text("begin") + nl() + self.entrypoint().to_pretty_print(&self.mast).render(),
206        ) + nl()
207            + const_text("end")
208    }
209}
210
211#[cfg(test)]
215mod tests {
216
217    use miden_core::Felt;
218    use miden_crypto::utils::{Deserializable, Serializable};
219
220    use crate::account::{AccountCode, AccountProcedureInfo};
221
222    #[test]
223    fn test_from_to_account_procedure() {
224        let account_code = AccountCode::mock();
225
226        let procedure = account_code.procedures()[0];
227
228        let felts: [Felt; 8] = procedure.into();
230
231        let final_procedure: AccountProcedureInfo = felts.try_into().unwrap();
233
234        assert_eq!(procedure, final_procedure);
235    }
236
237    #[test]
238    fn test_serde_account_procedure() {
239        let account_code = AccountCode::mock();
240
241        let serialized = account_code.procedures()[0].to_bytes();
242        let deserialized = AccountProcedureInfo::read_from_bytes(&serialized).unwrap();
243
244        assert_eq!(account_code.procedures()[0], deserialized);
245    }
246}