miden_objects/account/component/
mod.rs

1use alloc::collections::BTreeSet;
2use alloc::vec::Vec;
3
4use miden_assembly::ast::QualifiedProcedureName;
5use miden_assembly::{Assembler, Library, Parse};
6use miden_processor::MastForest;
7
8mod template;
9pub use template::*;
10
11use crate::account::{AccountType, StorageSlot};
12use crate::{AccountError, Word};
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 Parse,
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    /// Returns a vector of tuples (digest, is_auth) for all procedures in this component.
150    pub(crate) fn get_procedures(&self) -> Vec<(Word, bool)> {
151        let mut procedures = Vec::new();
152        for module in self.library.module_infos() {
153            for (_, procedure_info) in module.procedures() {
154                let is_auth = procedure_info.name.contains("auth__");
155                procedures.push((procedure_info.digest, is_auth));
156            }
157        }
158        procedures
159    }
160
161    /// Returns the digest of the procedure with the specified name, or `None` if it was not found
162    /// in this component's library or its library path is malformed.
163    pub fn get_procedure_root_by_name(
164        &self,
165        proc_name: impl TryInto<QualifiedProcedureName>,
166    ) -> Option<Word> {
167        self.library.get_procedure_root_by_name(proc_name)
168    }
169
170    // MUTATORS
171    // --------------------------------------------------------------------------------------------
172
173    /// Adds `supported_type` to the set of [`AccountType`]s supported by this component.
174    ///
175    /// This function has the semantics of [`BTreeSet::insert`], i.e. adding a type twice is fine
176    /// and it can be called multiple times with different account types.
177    pub fn with_supported_type(mut self, supported_type: AccountType) -> Self {
178        self.supported_types.insert(supported_type);
179        self
180    }
181
182    /// Overwrites any previously set supported types with the given set.
183    ///
184    /// This can be used to reset the supported types of a component to a chosen set, which may be
185    /// useful after cloning an existing component.
186    pub fn with_supported_types(mut self, supported_types: BTreeSet<AccountType>) -> Self {
187        self.supported_types = supported_types;
188        self
189    }
190
191    /// Sets the [`AccountType`]s supported by this component to all account types.
192    pub fn with_supports_all_types(mut self) -> Self {
193        self.supported_types.extend([
194            AccountType::FungibleFaucet,
195            AccountType::NonFungibleFaucet,
196            AccountType::RegularAccountImmutableCode,
197            AccountType::RegularAccountUpdatableCode,
198        ]);
199        self
200    }
201}
202
203impl From<AccountComponent> for Library {
204    fn from(component: AccountComponent) -> Self {
205        component.library
206    }
207}