miden_objects/account/storage/
mod.rs

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