miden_protocol/account/component/
mod.rs1use alloc::collections::BTreeSet;
2use alloc::vec::Vec;
3
4use miden_mast_package::{MastArtifact, Package};
5
6mod metadata;
7pub use metadata::*;
8
9pub mod storage;
10pub use storage::*;
11
12mod code;
13pub use code::AccountComponentCode;
14
15use crate::account::{AccountType, StorageSlot};
16use crate::assembly::Path;
17use crate::errors::AccountError;
18use crate::{MastForest, Word};
19
20#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct AccountComponent {
40 pub(super) code: AccountComponentCode,
41 pub(super) storage_slots: Vec<StorageSlot>,
42 pub(super) metadata: Option<AccountComponentMetadata>,
43 pub(super) supported_types: BTreeSet<AccountType>,
44}
45
46impl AccountComponent {
47 pub fn new(
65 code: impl Into<AccountComponentCode>,
66 storage_slots: Vec<StorageSlot>,
67 ) -> Result<Self, AccountError> {
68 u8::try_from(storage_slots.len())
70 .map_err(|_| AccountError::StorageTooManySlots(storage_slots.len() as u64))?;
71
72 Ok(Self {
73 code: code.into(),
74 storage_slots,
75 metadata: None,
76 supported_types: BTreeSet::new(),
77 })
78 }
79
80 pub fn from_package(
100 package: &Package,
101 init_storage_data: &InitStorageData,
102 ) -> Result<Self, AccountError> {
103 let metadata = AccountComponentMetadata::try_from(package)?;
104 let library = match &package.mast {
105 MastArtifact::Library(library) => library.as_ref().clone(),
106 MastArtifact::Executable(_) => {
107 return Err(AccountError::other(
108 "expected Package to contain a library, but got an executable",
109 ));
110 },
111 };
112
113 let component_code = AccountComponentCode::from(library);
114 Self::from_library(&component_code, &metadata, init_storage_data)
115 }
116
117 pub fn from_library(
139 library: &AccountComponentCode,
140 account_component_metadata: &AccountComponentMetadata,
141 init_storage_data: &InitStorageData,
142 ) -> Result<Self, AccountError> {
143 let storage_slots = account_component_metadata
144 .storage_schema()
145 .build_storage_slots(init_storage_data)
146 .map_err(|err| {
147 AccountError::other_with_source("failed to instantiate account component", err)
148 })?;
149
150 Ok(AccountComponent::new(library.clone(), storage_slots)?
151 .with_metadata(account_component_metadata.clone()))
152 }
153
154 pub fn storage_size(&self) -> u8 {
159 u8::try_from(self.storage_slots.len())
160 .expect("storage slots len should fit in u8 per the constructor")
161 }
162
163 pub fn component_code(&self) -> &AccountComponentCode {
165 &self.code
166 }
167
168 pub fn mast_forest(&self) -> &MastForest {
170 self.code.mast_forest()
171 }
172
173 pub fn storage_slots(&self) -> &[StorageSlot] {
175 self.storage_slots.as_slice()
176 }
177
178 pub fn metadata(&self) -> Option<&AccountComponentMetadata> {
180 self.metadata.as_ref()
181 }
182
183 pub fn storage_schema(&self) -> Option<&StorageSchema> {
185 self.metadata.as_ref().map(AccountComponentMetadata::storage_schema)
186 }
187
188 pub fn supported_types(&self) -> &BTreeSet<AccountType> {
190 &self.supported_types
191 }
192
193 pub fn supports_type(&self, account_type: AccountType) -> bool {
195 self.supported_types.contains(&account_type)
196 }
197
198 pub fn get_procedures(&self) -> Vec<(Word, bool)> {
200 let mut procedures = Vec::new();
201 for module in self.code.as_library().module_infos() {
202 for (_, procedure_info) in module.procedures() {
203 let is_auth = procedure_info.name.starts_with("auth_");
204 procedures.push((procedure_info.digest, is_auth));
205 }
206 }
207 procedures
208 }
209
210 pub fn get_procedure_root_by_path(&self, proc_name: impl AsRef<Path>) -> Option<Word> {
213 self.code.as_library().get_procedure_root_by_path(proc_name)
214 }
215
216 pub fn with_supported_type(mut self, supported_type: AccountType) -> Self {
224 self.supported_types.insert(supported_type);
225 self
226 }
227
228 pub fn with_supported_types(mut self, supported_types: BTreeSet<AccountType>) -> Self {
233 self.supported_types = supported_types;
234 self
235 }
236
237 pub fn with_metadata(mut self, metadata: AccountComponentMetadata) -> Self {
239 self.supported_types = metadata.supported_types().clone();
240 self.metadata = Some(metadata);
241 self
242 }
243
244 pub fn with_supports_all_types(mut self) -> Self {
246 self.supported_types.extend([
247 AccountType::FungibleFaucet,
248 AccountType::NonFungibleFaucet,
249 AccountType::RegularAccountImmutableCode,
250 AccountType::RegularAccountUpdatableCode,
251 ]);
252 self
253 }
254}
255
256impl From<AccountComponent> for AccountComponentCode {
257 fn from(component: AccountComponent) -> Self {
258 component.code
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use alloc::collections::BTreeSet;
265 use alloc::string::ToString;
266 use alloc::sync::Arc;
267
268 use miden_assembly::Assembler;
269 use miden_core::utils::Serializable;
270 use miden_mast_package::{
271 MastArtifact,
272 Package,
273 PackageKind,
274 PackageManifest,
275 Section,
276 SectionId,
277 };
278 use semver::Version;
279
280 use super::*;
281 use crate::testing::account_code::CODE;
282
283 #[test]
284 fn test_extract_metadata_from_package() {
285 let library = Assembler::default().assemble_library([CODE]).unwrap();
287
288 let metadata = AccountComponentMetadata::new(
290 "test_component".to_string(),
291 "A test component".to_string(),
292 Version::new(1, 0, 0),
293 BTreeSet::from_iter([AccountType::RegularAccountImmutableCode]),
294 StorageSchema::default(),
295 );
296
297 let metadata_bytes = metadata.to_bytes();
298 let package_with_metadata = Package {
299 name: "test_package".to_string(),
300 mast: MastArtifact::Library(Arc::new(library.clone())),
301 manifest: PackageManifest::new(None),
302 kind: PackageKind::AccountComponent,
303 sections: vec![Section::new(
304 SectionId::ACCOUNT_COMPONENT_METADATA,
305 metadata_bytes.clone(),
306 )],
307 version: Default::default(),
308 description: None,
309 };
310
311 let extracted_metadata =
312 AccountComponentMetadata::try_from(&package_with_metadata).unwrap();
313 assert_eq!(extracted_metadata.name(), "test_component");
314 assert!(
315 extracted_metadata
316 .supported_types()
317 .contains(&AccountType::RegularAccountImmutableCode)
318 );
319
320 let package_without_metadata = Package {
322 name: "test_package_no_metadata".to_string(),
323 mast: MastArtifact::Library(Arc::new(library)),
324 manifest: PackageManifest::new(None),
325 kind: PackageKind::AccountComponent,
326 sections: vec![], version: Default::default(),
328 description: None,
329 };
330
331 let result = AccountComponentMetadata::try_from(&package_without_metadata);
332 assert!(result.is_err());
333 let error_msg = result.unwrap_err().to_string();
334 assert!(error_msg.contains("package does not contain account component metadata"));
335 }
336
337 #[test]
338 fn test_from_library_with_init_data() {
339 let library = Assembler::default().assemble_library([CODE]).unwrap();
341 let component_code = AccountComponentCode::from(library.clone());
342
343 let metadata = AccountComponentMetadata::new(
345 "test_component".to_string(),
346 "A test component".to_string(),
347 Version::new(1, 0, 0),
348 BTreeSet::from_iter([
349 AccountType::RegularAccountImmutableCode,
350 AccountType::RegularAccountUpdatableCode,
351 ]),
352 StorageSchema::default(),
353 );
354
355 let init_data = InitStorageData::default();
358 let component =
359 AccountComponent::from_library(&component_code, &metadata, &init_data).unwrap();
360
361 assert_eq!(component.storage_size(), 0);
363 assert!(component.supports_type(AccountType::RegularAccountImmutableCode));
364 assert!(component.supports_type(AccountType::RegularAccountUpdatableCode));
365 assert!(!component.supports_type(AccountType::FungibleFaucet));
366
367 let package_without_metadata = Package {
369 name: "test_package_no_metadata".to_string(),
370 mast: MastArtifact::Library(Arc::new(library)),
371 kind: PackageKind::AccountComponent,
372 manifest: PackageManifest::new(None),
373 sections: vec![], version: Default::default(),
375 description: None,
376 };
377
378 let result = AccountComponent::from_package(&package_without_metadata, &init_data);
379 assert!(result.is_err());
380 let error_msg = result.unwrap_err().to_string();
381 assert!(error_msg.contains("package does not contain account component metadata"));
382 }
383}