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