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