miden_objects/account/code/
procedure.rs1use alloc::string::ToString;
2
3use vm_core::{
4 utils::{ByteReader, ByteWriter, Deserializable, Serializable},
5 FieldElement,
6};
7use vm_processor::DeserializationError;
8
9use super::{Digest, Felt};
10use crate::AccountError;
11
12#[derive(Debug, PartialEq, Eq, Clone)]
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#[cfg(test)]
159mod tests {
160
161 use miden_crypto::utils::{Deserializable, Serializable};
162 use vm_core::Felt;
163
164 use crate::account::{AccountCode, AccountProcedureInfo};
165
166 #[test]
167 fn test_from_to_account_procedure() {
168 let account_code = AccountCode::mock();
169
170 let procedure = account_code.procedures()[0].clone();
171
172 let felts: [Felt; 8] = procedure.clone().into();
174
175 let final_procedure: AccountProcedureInfo = felts.try_into().unwrap();
177
178 assert_eq!(procedure, final_procedure);
179 }
180
181 #[test]
182 fn test_serde_account_procedure() {
183 let account_code = AccountCode::mock();
184
185 let serialized = account_code.procedures()[0].to_bytes();
186 let deserialized = AccountProcedureInfo::read_from_bytes(&serialized).unwrap();
187
188 assert_eq!(account_code.procedures()[0], deserialized);
189 }
190}