miden_objects/account/storage/
mod.rs

1use alloc::{string::ToString, vec::Vec};
2
3use super::{
4    AccountError, AccountStorageDelta, ByteReader, ByteWriter, Deserializable,
5    DeserializationError, Digest, Felt, Hasher, Serializable, Word,
6};
7use crate::account::{AccountComponent, AccountType};
8
9mod slot;
10pub use slot::{StorageSlot, StorageSlotType};
11
12mod map;
13pub use map::StorageMap;
14
15mod header;
16pub use header::{AccountStorageHeader, StorageSlotHeader};
17
18// ACCOUNT STORAGE
19// ================================================================================================
20
21/// Account storage is composed of a variable number of index-addressable [StorageSlot]s up to
22/// 255 slots in total.
23///
24/// Each slot has a type which defines its size and structure. Currently, the following types are
25/// supported:
26/// - [StorageSlot::Value]: contains a single [Word] of data (i.e., 32 bytes).
27/// - [StorageSlot::Map]: contains a [StorageMap] which is a key-value map where both keys and
28///   values are [Word]s. The value of a storage slot containing a map is the commitment to the
29///   underlying map.
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct AccountStorage {
32    slots: Vec<StorageSlot>,
33}
34
35impl AccountStorage {
36    /// The maximum number of storage slots allowed in an account storage.
37    pub const MAX_NUM_STORAGE_SLOTS: usize = 255;
38
39    // CONSTRUCTOR
40    // --------------------------------------------------------------------------------------------
41
42    /// Returns a new instance of account storage initialized with the provided items.
43    ///
44    /// # Errors
45    ///
46    /// Returns an error if:
47    /// - The number of [`StorageSlot`]s exceeds 255.
48    pub fn new(slots: Vec<StorageSlot>) -> Result<AccountStorage, AccountError> {
49        let num_slots = slots.len();
50
51        if num_slots > Self::MAX_NUM_STORAGE_SLOTS {
52            return Err(AccountError::StorageTooManySlots(num_slots as u64));
53        }
54
55        Ok(Self { slots })
56    }
57
58    /// Creates an [`AccountStorage`] from the provided components' storage slots.
59    ///
60    /// If the account type is faucet the reserved slot (slot 0) will be initialized.
61    /// - For Fungible Faucets the value is [`StorageSlot::empty_value`].
62    /// - For Non-Fungible Faucets the value is [`StorageSlot::empty_map`].
63    ///
64    /// If the storage needs to be initialized with certain values in that slot, those can be added
65    /// after construction with the standard set methods for items and maps.
66    ///
67    /// # Errors
68    ///
69    /// Returns an error if:
70    /// - The number of [`StorageSlot`]s of all components exceeds 255.
71    pub(super) fn from_components(
72        components: &[AccountComponent],
73        account_type: AccountType,
74    ) -> Result<AccountStorage, AccountError> {
75        let mut storage_slots = match account_type {
76            AccountType::FungibleFaucet => vec![StorageSlot::empty_value()],
77            AccountType::NonFungibleFaucet => vec![StorageSlot::empty_map()],
78            _ => vec![],
79        };
80
81        storage_slots
82            .extend(components.iter().flat_map(|component| component.storage_slots()).cloned());
83
84        Self::new(storage_slots)
85    }
86
87    // PUBLIC ACCESSORS
88    // --------------------------------------------------------------------------------------------
89
90    /// Returns a reference to the storage slots.
91    pub fn slots(&self) -> &Vec<StorageSlot> {
92        &self.slots
93    }
94
95    /// Returns a commitment to this storage.
96    pub fn commitment(&self) -> Digest {
97        build_slots_commitment(&self.slots)
98    }
99
100    /// Converts storage slots of this account storage into a vector of field elements.
101    ///
102    /// This is done by first converting each storage slot into exactly 8 elements as follows:
103    /// ```text
104    /// [STORAGE_SLOT_VALUE, storage_slot_type, 0, 0, 0]
105    /// ```
106    /// And then concatenating the resulting elements into a single vector.
107    pub fn as_elements(&self) -> Vec<Felt> {
108        slots_as_elements(self.slots())
109    }
110
111    /// Returns an item from the storage at the specified index.
112    ///
113    /// # Errors:
114    /// - If the index is out of bounds
115    pub fn get_item(&self, index: u8) -> Result<Digest, AccountError> {
116        self.slots
117            .get(index as usize)
118            .ok_or(AccountError::StorageIndexOutOfBounds {
119                slots_len: self.slots.len() as u8,
120                index,
121            })
122            .map(|slot| slot.value().into())
123    }
124
125    /// Returns a map item from a map located in storage at the specified index.
126    ///
127    /// # Errors:
128    /// - If the index is out of bounds
129    /// - If the [StorageSlot] is not [StorageSlotType::Map]
130    pub fn get_map_item(&self, index: u8, key: Word) -> Result<Word, AccountError> {
131        match self.slots.get(index as usize).ok_or(AccountError::StorageIndexOutOfBounds {
132            slots_len: self.slots.len() as u8,
133            index,
134        })? {
135            StorageSlot::Map(ref map) => Ok(map.get_value(&Digest::from(key))),
136            _ => Err(AccountError::StorageSlotNotMap(index)),
137        }
138    }
139
140    /// Returns an [AccountStorageHeader] for this account storage.
141    pub fn get_header(&self) -> AccountStorageHeader {
142        AccountStorageHeader::new(
143            self.slots.iter().map(|slot| (slot.slot_type(), slot.value())).collect(),
144        )
145    }
146
147    // DATA MUTATORS
148    // --------------------------------------------------------------------------------------------
149
150    /// Applies the provided delta to this account storage.
151    ///
152    /// # Errors:
153    /// - If the updates violate storage constraints.
154    pub(super) fn apply_delta(&mut self, delta: &AccountStorageDelta) -> Result<(), AccountError> {
155        let len = self.slots.len() as u8;
156
157        // update storage maps
158        for (&idx, map) in delta.maps().iter() {
159            let storage_slot = self
160                .slots
161                .get_mut(idx as usize)
162                .ok_or(AccountError::StorageIndexOutOfBounds { slots_len: len, index: idx })?;
163
164            let storage_map = match storage_slot {
165                StorageSlot::Map(map) => map,
166                _ => return Err(AccountError::StorageSlotNotMap(idx)),
167            };
168
169            storage_map.apply_delta(map);
170        }
171
172        // update storage values
173        for (&idx, &value) in delta.values().iter() {
174            self.set_item(idx, value)?;
175        }
176
177        Ok(())
178    }
179
180    /// Updates the value of the storage slot at the specified index.
181    ///
182    /// This method should be used only to update value slots. For updating values
183    /// in storage maps, please see [AccountStorage::set_map_item()].
184    ///
185    /// # Errors:
186    /// - If the index is out of bounds
187    /// - If the [StorageSlot] is not [StorageSlotType::Value]
188    pub fn set_item(&mut self, index: u8, value: Word) -> Result<Word, AccountError> {
189        // check if index is in bounds
190        let num_slots = self.slots.len();
191
192        if index as usize >= num_slots {
193            return Err(AccountError::StorageIndexOutOfBounds {
194                slots_len: self.slots.len() as u8,
195                index,
196            });
197        }
198
199        let old_value = match self.slots[index as usize] {
200            StorageSlot::Value(value) => value,
201            // return an error if the type != Value
202            _ => return Err(AccountError::StorageSlotNotValue(index)),
203        };
204
205        // update the value of the storage slot
206        self.slots[index as usize] = StorageSlot::Value(value);
207
208        Ok(old_value)
209    }
210
211    /// Updates the value of a key-value pair of a storage map at the specified index.
212    ///
213    /// This method should be used only to update storage maps. For updating values
214    /// in storage slots, please see [AccountStorage::set_item()].
215    ///
216    /// # Errors:
217    /// - If the index is out of bounds
218    /// - If the [StorageSlot] is not [StorageSlotType::Map]
219    pub fn set_map_item(
220        &mut self,
221        index: u8,
222        key: Word,
223        value: Word,
224    ) -> Result<(Word, Word), AccountError> {
225        // check if index is in bounds
226        let num_slots = self.slots.len();
227
228        if index as usize >= num_slots {
229            return Err(AccountError::StorageIndexOutOfBounds {
230                slots_len: self.slots.len() as u8,
231                index,
232            });
233        }
234
235        let storage_map = match self.slots[index as usize] {
236            StorageSlot::Map(ref mut map) => map,
237            _ => return Err(AccountError::StorageSlotNotMap(index)),
238        };
239
240        // get old map root to return
241        let old_root = storage_map.root();
242
243        // update the key-value pair in the map
244        let old_value = storage_map.insert(key.into(), value);
245
246        Ok((old_root.into(), old_value))
247    }
248}
249
250// ITERATORS
251// ================================================================================================
252
253impl IntoIterator for AccountStorage {
254    type Item = StorageSlot;
255    type IntoIter = alloc::vec::IntoIter<StorageSlot>;
256
257    fn into_iter(self) -> Self::IntoIter {
258        self.slots.into_iter()
259    }
260}
261
262// HELPER FUNCTIONS
263// ------------------------------------------------------------------------------------------------
264
265/// Converts given slots into field elements
266fn slots_as_elements(slots: &[StorageSlot]) -> Vec<Felt> {
267    slots
268        .iter()
269        .flat_map(|slot| StorageSlotHeader::from(slot).as_elements())
270        .collect()
271}
272
273/// Computes the commitment to the given slots
274fn build_slots_commitment(slots: &[StorageSlot]) -> Digest {
275    let elements = slots_as_elements(slots);
276    Hasher::hash_elements(&elements)
277}
278
279// SERIALIZATION
280// ================================================================================================
281
282impl Serializable for AccountStorage {
283    fn write_into<W: ByteWriter>(&self, target: &mut W) {
284        target.write_u8(self.slots().len() as u8);
285        target.write_many(self.slots());
286    }
287
288    fn get_size_hint(&self) -> usize {
289        // Size of the serialized slot length.
290        let u8_size = 0u8.get_size_hint();
291        let mut size = u8_size;
292
293        for slot in self.slots() {
294            size += slot.get_size_hint();
295        }
296
297        size
298    }
299}
300
301impl Deserializable for AccountStorage {
302    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
303        let num_slots = source.read_u8()? as usize;
304        let slots = source.read_many::<StorageSlot>(num_slots)?;
305
306        Self::new(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
307    }
308}
309
310// TESTS
311// ================================================================================================
312
313#[cfg(test)]
314mod tests {
315    use super::{
316        build_slots_commitment, AccountStorage, Deserializable, Serializable, StorageMap, Word,
317    };
318    use crate::account::StorageSlot;
319
320    #[test]
321    fn test_serde_account_storage() {
322        // empty storage
323        let storage = AccountStorage::new(vec![]).unwrap();
324        let bytes = storage.to_bytes();
325        assert_eq!(storage, AccountStorage::read_from_bytes(&bytes).unwrap());
326
327        // storage with values for default types
328        let storage = AccountStorage::new(vec![
329            StorageSlot::Value(Word::default()),
330            StorageSlot::Map(StorageMap::default()),
331        ])
332        .unwrap();
333        let bytes = storage.to_bytes();
334        assert_eq!(storage, AccountStorage::read_from_bytes(&bytes).unwrap());
335    }
336
337    #[test]
338    fn test_account_storage_slots_commitment() {
339        let storage = AccountStorage::mock();
340        let storage_slots_commitment = build_slots_commitment(storage.slots());
341        assert_eq!(storage_slots_commitment, storage.commitment())
342    }
343}