miden_objects/account/code/
procedure.rs1use alloc::{string::ToString, sync::Arc};
2
3use vm_core::{mast::MastForest, prettier::PrettyPrint};
4use vm_processor::{MastNode, MastNodeId};
5
6use super::{Digest, Felt};
7use crate::{
8 AccountError, FieldElement,
9 utils::serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
10};
11
12#[derive(Debug, PartialEq, Eq, Clone, Copy)]
31pub struct AccountProcedureInfo {
32 mast_root: Digest,
33 storage_offset: u8,
34 storage_size: u8,
35}
36
37impl AccountProcedureInfo {
38 pub const NUM_ELEMENTS_PER_PROC: usize = 8;
40
41 pub fn new(
50 mast_root: Digest,
51 storage_offset: u8,
52 storage_size: u8,
53 ) -> Result<Self, AccountError> {
54 if storage_size == 0 && storage_offset != 0 {
55 return Err(AccountError::PureProcedureWithStorageOffset);
56 }
57
58 if storage_offset.checked_add(storage_size).is_none() {
61 return Err(AccountError::StorageOffsetPlusSizeOutOfBounds(
62 storage_offset as u16 + storage_size as u16,
63 ));
64 }
65
66 Ok(Self { mast_root, storage_offset, storage_size })
67 }
68
69 pub fn mast_root(&self) -> &Digest {
74 &self.mast_root
75 }
76
77 pub fn storage_offset(&self) -> u8 {
79 self.storage_offset
80 }
81
82 pub fn storage_size(&self) -> u8 {
84 self.storage_size
85 }
86}
87
88impl From<AccountProcedureInfo> for [Felt; 8] {
89 fn from(value: AccountProcedureInfo) -> Self {
90 let mut result = [Felt::ZERO; 8];
91
92 result[0..4].copy_from_slice(value.mast_root().as_elements());
94
95 result[4] = Felt::from(value.storage_offset);
97
98 result[5] = Felt::from(value.storage_size);
100
101 result
102 }
103}
104
105impl TryFrom<[Felt; 8]> for AccountProcedureInfo {
106 type Error = AccountError;
107
108 fn try_from(value: [Felt; 8]) -> Result<Self, Self::Error> {
109 let mast_root = Digest::from(<[Felt; 4]>::try_from(&value[0..4]).unwrap());
111
112 let storage_offset: u8 = value[4].try_into().map_err(|_| {
114 AccountError::AccountCodeProcedureStorageOffsetTooLarge(mast_root, value[4])
115 })?;
116
117 let storage_size: u8 = value[5].try_into().map_err(|_| {
119 AccountError::AccountCodeProcedureStorageSizeTooLarge(mast_root, value[5])
120 })?;
121
122 if value[6] != Felt::ZERO || value[7] != Felt::ZERO {
124 return Err(AccountError::AccountCodeProcedureInvalidPadding(mast_root));
125 }
126
127 Ok(Self { mast_root, storage_offset, storage_size })
128 }
129}
130
131impl Serializable for AccountProcedureInfo {
132 fn write_into<W: ByteWriter>(&self, target: &mut W) {
133 target.write(self.mast_root);
134 target.write_u8(self.storage_offset);
135 target.write_u8(self.storage_size)
136 }
137
138 fn get_size_hint(&self) -> usize {
139 self.mast_root.get_size_hint()
140 + self.storage_offset.get_size_hint()
141 + self.storage_size.get_size_hint()
142 }
143}
144
145impl Deserializable for AccountProcedureInfo {
146 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
147 let mast_root: Digest = source.read()?;
148 let storage_offset = source.read_u8()?;
149 let storage_size = source.read_u8()?;
150 Self::new(mast_root, storage_offset, storage_size)
151 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
152 }
153}
154
155#[derive(Debug, Clone)]
160pub struct PrintableProcedure {
161 mast: Arc<MastForest>,
162 procedure_info: AccountProcedureInfo,
163 entrypoint: MastNodeId,
164}
165
166impl PrintableProcedure {
167 pub(crate) fn new(
169 mast: Arc<MastForest>,
170 procedure_info: AccountProcedureInfo,
171 entrypoint: MastNodeId,
172 ) -> Self {
173 Self { mast, procedure_info, entrypoint }
174 }
175
176 fn entrypoint(&self) -> &MastNode {
177 &self.mast[self.entrypoint]
178 }
179
180 pub(crate) fn storage_offset(&self) -> u8 {
181 self.procedure_info.storage_offset()
182 }
183
184 pub(crate) fn storage_size(&self) -> u8 {
185 self.procedure_info.storage_size()
186 }
187
188 pub(crate) fn mast_root(&self) -> &Digest {
189 self.procedure_info.mast_root()
190 }
191}
192
193impl PrettyPrint for PrintableProcedure {
194 fn render(&self) -> vm_core::prettier::Document {
195 use vm_core::prettier::*;
196
197 indent(
198 4,
199 const_text("begin") + nl() + self.entrypoint().to_pretty_print(&self.mast).render(),
200 ) + nl()
201 + const_text("end")
202 }
203}
204
205#[cfg(test)]
209mod tests {
210
211 use miden_crypto::utils::{Deserializable, Serializable};
212 use vm_core::Felt;
213
214 use crate::account::{AccountCode, AccountProcedureInfo};
215
216 #[test]
217 fn test_from_to_account_procedure() {
218 let account_code = AccountCode::mock();
219
220 let procedure = account_code.procedures()[0];
221
222 let felts: [Felt; 8] = procedure.into();
224
225 let final_procedure: AccountProcedureInfo = felts.try_into().unwrap();
227
228 assert_eq!(procedure, final_procedure);
229 }
230
231 #[test]
232 fn test_serde_account_procedure() {
233 let account_code = AccountCode::mock();
234
235 let serialized = account_code.procedures()[0].to_bytes();
236 let deserialized = AccountProcedureInfo::read_from_bytes(&serialized).unwrap();
237
238 assert_eq!(account_code.procedures()[0], deserialized);
239 }
240}