miden_objects/account/code/
procedure.rs1use alloc::string::ToString;
2
3use super::{Digest, Felt};
4use crate::{
5 AccountError, FieldElement,
6 utils::serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8
9#[derive(Debug, PartialEq, Eq, Clone, Copy)]
28pub struct AccountProcedureInfo {
29 mast_root: Digest,
30 storage_offset: u8,
31 storage_size: u8,
32}
33
34impl AccountProcedureInfo {
35 pub const NUM_ELEMENTS_PER_PROC: usize = 8;
37
38 pub fn new(
47 mast_root: Digest,
48 storage_offset: u8,
49 storage_size: u8,
50 ) -> Result<Self, AccountError> {
51 if storage_size == 0 && storage_offset != 0 {
52 return Err(AccountError::PureProcedureWithStorageOffset);
53 }
54
55 if storage_offset.checked_add(storage_size).is_none() {
58 return Err(AccountError::StorageOffsetPlusSizeOutOfBounds(
59 storage_offset as u16 + storage_size as u16,
60 ));
61 }
62
63 Ok(Self { mast_root, storage_offset, storage_size })
64 }
65
66 pub fn mast_root(&self) -> &Digest {
71 &self.mast_root
72 }
73
74 pub fn storage_offset(&self) -> u8 {
76 self.storage_offset
77 }
78
79 pub fn storage_size(&self) -> u8 {
81 self.storage_size
82 }
83}
84
85impl From<AccountProcedureInfo> for [Felt; 8] {
86 fn from(value: AccountProcedureInfo) -> Self {
87 let mut result = [Felt::ZERO; 8];
88
89 result[0..4].copy_from_slice(value.mast_root().as_elements());
91
92 result[4] = Felt::from(value.storage_offset);
94
95 result[5] = Felt::from(value.storage_size);
97
98 result
99 }
100}
101
102impl TryFrom<[Felt; 8]> for AccountProcedureInfo {
103 type Error = AccountError;
104
105 fn try_from(value: [Felt; 8]) -> Result<Self, Self::Error> {
106 let mast_root = Digest::from(<[Felt; 4]>::try_from(&value[0..4]).unwrap());
108
109 let storage_offset: u8 = value[4].try_into().map_err(|_| {
111 AccountError::AccountCodeProcedureStorageOffsetTooLarge(mast_root, value[4])
112 })?;
113
114 let storage_size: u8 = value[5].try_into().map_err(|_| {
116 AccountError::AccountCodeProcedureStorageSizeTooLarge(mast_root, value[5])
117 })?;
118
119 if value[6] != Felt::ZERO || value[7] != Felt::ZERO {
121 return Err(AccountError::AccountCodeProcedureInvalidPadding(mast_root));
122 }
123
124 Ok(Self { mast_root, storage_offset, storage_size })
125 }
126}
127
128impl Serializable for AccountProcedureInfo {
129 fn write_into<W: ByteWriter>(&self, target: &mut W) {
130 target.write(self.mast_root);
131 target.write_u8(self.storage_offset);
132 target.write_u8(self.storage_size)
133 }
134
135 fn get_size_hint(&self) -> usize {
136 self.mast_root.get_size_hint()
137 + self.storage_offset.get_size_hint()
138 + self.storage_size.get_size_hint()
139 }
140}
141
142impl Deserializable for AccountProcedureInfo {
143 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
144 let mast_root: Digest = source.read()?;
145 let storage_offset = source.read_u8()?;
146 let storage_size = source.read_u8()?;
147 Self::new(mast_root, storage_offset, storage_size)
148 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
149 }
150}
151
152#[cfg(test)]
156mod tests {
157
158 use miden_crypto::utils::{Deserializable, Serializable};
159 use vm_core::Felt;
160
161 use crate::account::{AccountCode, AccountProcedureInfo};
162
163 #[test]
164 fn test_from_to_account_procedure() {
165 let account_code = AccountCode::mock();
166
167 let procedure = account_code.procedures()[0];
168
169 let felts: [Felt; 8] = procedure.into();
171
172 let final_procedure: AccountProcedureInfo = felts.try_into().unwrap();
174
175 assert_eq!(procedure, final_procedure);
176 }
177
178 #[test]
179 fn test_serde_account_procedure() {
180 let account_code = AccountCode::mock();
181
182 let serialized = account_code.procedures()[0].to_bytes();
183 let deserialized = AccountProcedureInfo::read_from_bytes(&serialized).unwrap();
184
185 assert_eq!(account_code.procedures()[0], deserialized);
186 }
187}