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