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}