miden_objects/account/component/mod.rs
1use alloc::{collections::BTreeSet, vec::Vec};
2
3use assembly::{Assembler, Compile, Library};
4use vm_processor::MastForest;
5
6mod template;
7pub use template::*;
8
9use crate::{
10 AccountError,
11 account::{AccountType, StorageSlot},
12};
13
14/// An [`AccountComponent`] defines a [`Library`] of code and the initial value and types of
15/// the [`StorageSlot`]s it accesses.
16///
17/// One or more components can be used to built [`AccountCode`](crate::account::AccountCode) and
18/// [`AccountStorage`](crate::account::AccountStorage).
19///
20/// Each component is independent of other components and can only access its own storage slots.
21/// Each component defines its own storage layout starting at index 0 up to the length of the
22/// storage slots vector.
23///
24/// Components define the [`AccountType`]s they support, meaning whether the component can be used
25/// to instantiate an account of that type. For example, a component implementing a fungible faucet
26/// would only specify support for [`AccountType::FungibleFaucet`]. Using it to instantiate a
27/// regular account would fail. By default, the set of supported types is empty, so each component
28/// is forced to explicitly define what it supports.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct AccountComponent {
31 pub(super) library: Library,
32 pub(super) storage_slots: Vec<StorageSlot>,
33 pub(super) supported_types: BTreeSet<AccountType>,
34}
35
36impl AccountComponent {
37 // CONSTRUCTORS
38 // --------------------------------------------------------------------------------------------
39
40 /// Returns a new [`AccountComponent`] constructed from the provided `library` and
41 /// `storage_slots`.
42 ///
43 /// All procedures exported from the provided code will become members of the account's public
44 /// interface when added to an [`AccountCode`](crate::account::AccountCode).
45 ///
46 /// # Errors
47 ///
48 /// The following list of errors is exhaustive and can be relied upon for `expect`ing the call
49 /// to this function. It is recommended that custom components ensure these conditions by design
50 /// or in their fallible constructors.
51 ///
52 /// Returns an error if:
53 /// - The number of given [`StorageSlot`]s exceeds 255.
54 pub fn new(code: Library, storage_slots: Vec<StorageSlot>) -> Result<Self, AccountError> {
55 // Check that we have less than 256 storage slots.
56 u8::try_from(storage_slots.len())
57 .map_err(|_| AccountError::StorageTooManySlots(storage_slots.len() as u64))?;
58
59 Ok(Self {
60 library: code,
61 storage_slots,
62 supported_types: BTreeSet::new(),
63 })
64 }
65
66 /// Returns a new [`AccountComponent`] whose library is compiled from the provided `source_code`
67 /// using the specified `assembler` and with the given `storage_slots`.
68 ///
69 /// All procedures exported from the provided code will become members of the account's public
70 /// interface when added to an [`AccountCode`](crate::account::AccountCode).
71 ///
72 /// # Errors
73 ///
74 /// Returns an error if:
75 /// - the compilation of the provided source code fails.
76 /// - The number of storage slots exceeds 255.
77 pub fn compile(
78 source_code: impl Compile,
79 assembler: Assembler,
80 storage_slots: Vec<StorageSlot>,
81 ) -> Result<Self, AccountError> {
82 let library = assembler
83 .assemble_library([source_code])
84 .map_err(AccountError::AccountComponentAssemblyError)?;
85
86 Self::new(library, storage_slots)
87 }
88
89 /// Instantiates an [AccountComponent] from the [AccountComponentTemplate].
90 ///
91 /// The template's component metadata might contain placeholders, which can be replaced by
92 /// mapping storage placeholders to values through the `init_storage_data` parameter.
93 ///
94 /// # Errors
95 ///
96 /// - If any of the component's storage entries cannot be transformed into a valid storage slot.
97 /// This could be because the metadata is invalid, or storage values were not provided (or
98 /// they are not of a valid type)
99 pub fn from_template(
100 template: &AccountComponentTemplate,
101 init_storage_data: &InitStorageData,
102 ) -> Result<AccountComponent, AccountError> {
103 let mut storage_slots = vec![];
104 for storage_entry in template.metadata().storage_entries() {
105 let entry_storage_slots = storage_entry
106 .try_build_storage_slots(init_storage_data)
107 .map_err(AccountError::AccountComponentTemplateInstantiationError)?;
108 storage_slots.extend(entry_storage_slots);
109 }
110
111 Ok(AccountComponent::new(template.library().clone(), storage_slots)?
112 .with_supported_types(template.metadata().supported_types().clone()))
113 }
114
115 // ACCESSORS
116 // --------------------------------------------------------------------------------------------
117
118 /// Returns the number of storage slots accessible from this component.
119 pub fn storage_size(&self) -> u8 {
120 u8::try_from(self.storage_slots.len())
121 .expect("storage slots len should fit in u8 per the constructor")
122 }
123
124 /// Returns a reference to the underlying [`Library`] of this component.
125 pub fn library(&self) -> &Library {
126 &self.library
127 }
128
129 /// Returns a reference to the underlying [`MastForest`] of this component.
130 pub fn mast_forest(&self) -> &MastForest {
131 self.library.mast_forest().as_ref()
132 }
133
134 /// Returns a slice of the underlying [`StorageSlot`]s of this component.
135 pub fn storage_slots(&self) -> &[StorageSlot] {
136 self.storage_slots.as_slice()
137 }
138
139 /// Returns a reference to the supported [`AccountType`]s.
140 pub fn supported_types(&self) -> &BTreeSet<AccountType> {
141 &self.supported_types
142 }
143
144 /// Returns `true` if this component supports the given `account_type`, `false` otherwise.
145 pub fn supports_type(&self, account_type: AccountType) -> bool {
146 self.supported_types.contains(&account_type)
147 }
148
149 // MUTATORS
150 // --------------------------------------------------------------------------------------------
151
152 /// Adds `supported_type` to the set of [`AccountType`]s supported by this component.
153 ///
154 /// This function has the semantics of [`BTreeSet::insert`], i.e. adding a type twice is fine
155 /// and it can be called multiple times with different account types.
156 pub fn with_supported_type(mut self, supported_type: AccountType) -> Self {
157 self.supported_types.insert(supported_type);
158 self
159 }
160
161 /// Overwrites any previously set supported types with the given set.
162 ///
163 /// This can be used to reset the supported types of a component to a chosen set, which may be
164 /// useful after cloning an existing component.
165 pub fn with_supported_types(mut self, supported_types: BTreeSet<AccountType>) -> Self {
166 self.supported_types = supported_types;
167 self
168 }
169
170 /// Sets the [`AccountType`]s supported by this component to all account types.
171 pub fn with_supports_all_types(mut self) -> Self {
172 self.supported_types.extend([
173 AccountType::FungibleFaucet,
174 AccountType::NonFungibleFaucet,
175 AccountType::RegularAccountImmutableCode,
176 AccountType::RegularAccountUpdatableCode,
177 ]);
178 self
179 }
180}
181
182impl From<AccountComponent> for Library {
183 fn from(component: AccountComponent) -> Self {
184 component.library
185 }
186}