miden_protocol/account/component/
mod.rs1use alloc::vec::Vec;
2
3use miden_mast_package::Package;
4use miden_processor::mast::MastNodeExt;
5
6mod metadata;
7pub use metadata::*;
8
9pub mod storage;
10pub use storage::*;
11
12mod code;
13pub use code::AccountComponentCode;
14
15use crate::MastForest;
16use crate::account::{AccountProcedureRoot, StorageSlot};
17use crate::assembly::Path;
18use crate::errors::AccountError;
19
20const AUTH_SCRIPT_ATTRIBUTE: &str = "auth_script";
22
23#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct AccountComponent {
37 pub(super) code: AccountComponentCode,
38 pub(super) storage_slots: Vec<StorageSlot>,
39 pub(super) metadata: AccountComponentMetadata,
40}
41
42impl AccountComponent {
43 pub fn new(
61 code: impl Into<AccountComponentCode>,
62 storage_slots: Vec<StorageSlot>,
63 metadata: AccountComponentMetadata,
64 ) -> Result<Self, AccountError> {
65 u8::try_from(storage_slots.len())
67 .map_err(|_| AccountError::StorageTooManySlots(storage_slots.len() as u64))?;
68
69 Ok(Self {
70 code: code.into(),
71 storage_slots,
72 metadata,
73 })
74 }
75
76 pub fn from_package(
96 package: &Package,
97 init_storage_data: &InitStorageData,
98 ) -> Result<Self, AccountError> {
99 let metadata = AccountComponentMetadata::try_from(package)?;
100 let library = package.mast.as_ref().clone();
101
102 let component_code = AccountComponentCode::from(library);
103 Self::from_library(&component_code, &metadata, init_storage_data)
104 }
105
106 pub fn from_library(
127 library: &AccountComponentCode,
128 metadata: &AccountComponentMetadata,
129 init_storage_data: &InitStorageData,
130 ) -> Result<Self, AccountError> {
131 let storage_slots = metadata
132 .storage_schema()
133 .build_storage_slots(init_storage_data)
134 .map_err(|err| {
135 AccountError::other_with_source("failed to instantiate account component", err)
136 })?;
137
138 AccountComponent::new(library.clone(), storage_slots, metadata.clone())
139 }
140
141 pub fn storage_size(&self) -> u8 {
146 u8::try_from(self.storage_slots.len())
147 .expect("storage slots len should fit in u8 per the constructor")
148 }
149
150 pub fn component_code(&self) -> &AccountComponentCode {
152 &self.code
153 }
154
155 pub fn mast_forest(&self) -> &MastForest {
157 self.code.mast_forest()
158 }
159
160 pub fn storage_slots(&self) -> &[StorageSlot] {
162 self.storage_slots.as_slice()
163 }
164
165 pub fn metadata(&self) -> &AccountComponentMetadata {
167 &self.metadata
168 }
169
170 pub fn storage_schema(&self) -> &StorageSchema {
172 self.metadata.storage_schema()
173 }
174
175 pub fn procedures(&self) -> impl Iterator<Item = (AccountProcedureRoot, bool)> + '_ {
181 let library = self.code.as_library();
182 library.exports().filter_map(|export| {
183 export.as_procedure().map(|proc_export| {
184 let digest = library
185 .mast_forest()
186 .get_node_by_id(proc_export.node)
187 .expect("export node not in the forest")
188 .digest();
189 let is_auth = proc_export.attributes.has(AUTH_SCRIPT_ATTRIBUTE);
190 (AccountProcedureRoot::from_raw(digest), is_auth)
191 })
192 })
193 }
194
195 pub fn get_procedure_root_by_path(
198 &self,
199 proc_name: impl AsRef<Path>,
200 ) -> Option<AccountProcedureRoot> {
201 self.code.get_procedure_root_by_path(proc_name)
202 }
203}
204
205impl From<AccountComponent> for AccountComponentCode {
206 fn from(component: AccountComponent) -> Self {
207 component.code
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use alloc::string::ToString;
214 use alloc::sync::Arc;
215
216 use miden_assembly::Assembler;
217 use miden_mast_package::{Package, PackageManifest, Section, SectionId, TargetType};
218 use semver::Version;
219
220 use super::*;
221 use crate::testing::account_code::CODE;
222 use crate::utils::serde::Serializable;
223
224 #[test]
225 fn test_extract_metadata_from_package() {
226 let library = Assembler::default().assemble_library([CODE]).unwrap();
228
229 let metadata = AccountComponentMetadata::new("test_component")
231 .with_description("A test component")
232 .with_version(Version::new(1, 0, 0));
233
234 let metadata_bytes = metadata.to_bytes();
235 let package_with_metadata = Package {
236 name: "test_package".into(),
237 mast: library.clone(),
238 manifest: PackageManifest::new(core::iter::empty()).unwrap(),
239 kind: TargetType::AccountComponent,
240 sections: vec![Section::new(
241 SectionId::ACCOUNT_COMPONENT_METADATA,
242 metadata_bytes.clone(),
243 )],
244 version: Version::new(0, 0, 0),
245 description: None,
246 };
247
248 let extracted_metadata =
249 AccountComponentMetadata::try_from(&package_with_metadata).unwrap();
250 assert_eq!(extracted_metadata.name(), "test_component");
251
252 let package_without_metadata = Package {
254 name: "test_package_no_metadata".into(),
255 mast: library,
256 manifest: PackageManifest::new(core::iter::empty()).unwrap(),
257 kind: TargetType::AccountComponent,
258 sections: vec![], version: Version::new(0, 0, 0),
260 description: None,
261 };
262
263 let result = AccountComponentMetadata::try_from(&package_without_metadata);
264 assert!(result.is_err());
265 let error_msg = result.unwrap_err().to_string();
266 assert!(error_msg.contains("package does not contain account component metadata"));
267 }
268
269 #[test]
270 fn test_from_library_with_init_data() {
271 let library = Assembler::default().assemble_library([CODE]).unwrap();
273 let component_code = AccountComponentCode::from(Arc::unwrap_or_clone(library.clone()));
274
275 let metadata = AccountComponentMetadata::new("test_component")
277 .with_description("A test component")
278 .with_version(Version::new(1, 0, 0));
279
280 let init_data = InitStorageData::default();
283 let component =
284 AccountComponent::from_library(&component_code, &metadata, &init_data).unwrap();
285
286 assert_eq!(component.storage_size(), 0);
288
289 let package_without_metadata = Package {
291 name: "test_package_no_metadata".into(),
292 mast: library,
293 kind: TargetType::AccountComponent,
294 manifest: PackageManifest::new(core::iter::empty()).unwrap(),
295 sections: vec![], version: Version::new(0, 0, 0),
297 description: None,
298 };
299
300 let result = AccountComponent::from_package(&package_without_metadata, &init_data);
301 assert!(result.is_err());
302 let error_msg = result.unwrap_err().to_string();
303 assert!(error_msg.contains("package does not contain account component metadata"));
304 }
305}