Skip to main content

miden_protocol/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    Serializable,
13    Word,
14};
15use crate::account::AccountComponent;
16use crate::crypto::SequentialCommit;
17
18mod slot;
19pub use slot::{StorageSlot, StorageSlotContent, StorageSlotId, StorageSlotName, StorageSlotType};
20
21mod map;
22pub use map::{PartialStorageMap, StorageMap, StorageMapKey, StorageMapKeyHash, 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 name-addressable [`StorageSlot`]s up to
34/// 255 slots in total.
35///
36/// Each slot consists of a [`StorageSlotName`] and [`StorageSlotContent`] which defines its size
37/// and structure. Currently, the following content types are supported:
38/// - [`StorageSlotContent::Value`]: contains a single [`Word`] of data (i.e., 32 bytes).
39/// - [`StorageSlotContent::Map`]: contains a [`StorageMap`] which is a key-value map where both
40///   keys and values are [Word]s. The value of a storage slot containing a map is the commitment to
41///   the underlying map.
42///
43/// Slots are sorted by [`StorageSlotName`] (or [`StorageSlotId`] equivalently). This order is
44/// necessary to:
45/// - Simplify lookups of slots in the transaction kernel (using `std::collections::sorted_array`
46///   from the miden core library)
47/// - Allow the [`AccountStorageDelta`] to work only with slot names instead of slot indices.
48/// - Make it simple to check for duplicates by iterating the slots and checking that no two
49///   adjacent items have the same slot name.
50#[derive(Debug, Clone, Default, PartialEq, Eq)]
51pub struct AccountStorage {
52    slots: Vec<StorageSlot>,
53}
54
55impl AccountStorage {
56    /// The maximum number of storage slots allowed in an account storage.
57    pub const MAX_NUM_STORAGE_SLOTS: usize = 255;
58
59    // CONSTRUCTOR
60    // --------------------------------------------------------------------------------------------
61
62    /// Returns a new instance of account storage initialized with the provided storage slots.
63    ///
64    /// This function sorts the slots by [`StorageSlotName`].
65    ///
66    /// # Errors
67    ///
68    /// Returns an error if:
69    /// - The number of [`StorageSlot`]s exceeds 255.
70    /// - There are multiple storage slots with the same [`StorageSlotName`].
71    pub fn new(mut slots: Vec<StorageSlot>) -> Result<AccountStorage, AccountError> {
72        let num_slots = slots.len();
73
74        if num_slots > Self::MAX_NUM_STORAGE_SLOTS {
75            return Err(AccountError::StorageTooManySlots(num_slots as u64));
76        }
77
78        // Unstable sort is fine because we require all names to be unique.
79        slots.sort_unstable();
80
81        // Check for slot name uniqueness by checking each neighboring slot's IDs. This is
82        // sufficient because the slots are sorted.
83        for slots in slots.windows(2) {
84            if slots[0].id() == slots[1].id() {
85                return Err(AccountError::DuplicateStorageSlotName(slots[0].name().clone()));
86            }
87        }
88
89        Ok(Self { slots })
90    }
91
92    /// Creates an [`AccountStorage`] from the provided components' storage slots.
93    ///
94    /// # Errors
95    ///
96    /// Returns an error if:
97    /// - The number of [`StorageSlot`]s of all components exceeds 255.
98    /// - There are multiple storage slots with the same [`StorageSlotName`].
99    pub(super) fn from_components(
100        components: Vec<AccountComponent>,
101    ) -> Result<AccountStorage, AccountError> {
102        let storage_slots = components
103            .into_iter()
104            .flat_map(|component| {
105                let AccountComponent { storage_slots, .. } = component;
106                storage_slots.into_iter()
107            })
108            .collect();
109
110        Self::new(storage_slots)
111    }
112
113    // PUBLIC ACCESSORS
114    // --------------------------------------------------------------------------------------------
115
116    /// Converts storage slots of this account storage into a vector of field elements.
117    ///
118    /// Each storage slot is represented by exactly 8 elements:
119    ///
120    /// ```text
121    /// [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE]
122    /// ```
123    pub fn to_elements(&self) -> Vec<Felt> {
124        <Self as SequentialCommit>::to_elements(self)
125    }
126
127    /// Returns the commitment to the [`AccountStorage`].
128    pub fn to_commitment(&self) -> Word {
129        <Self as SequentialCommit>::to_commitment(self)
130    }
131
132    /// Returns the number of slots in the account's storage.
133    pub fn num_slots(&self) -> u8 {
134        // SAFETY: The constructors of account storage ensure that the number of slots fits into a
135        // u8.
136        self.slots.len() as u8
137    }
138
139    /// Returns a reference to the storage slots.
140    pub fn slots(&self) -> &[StorageSlot] {
141        &self.slots
142    }
143
144    /// Consumes self and returns the storage slots of the account storage.
145    pub fn into_slots(self) -> Vec<StorageSlot> {
146        self.slots
147    }
148
149    /// Returns an [AccountStorageHeader] for this account storage.
150    pub fn to_header(&self) -> AccountStorageHeader {
151        AccountStorageHeader::new(self.slots.iter().map(StorageSlotHeader::from).collect())
152            .expect("slots should be valid as ensured by AccountStorage")
153    }
154
155    /// Returns a reference to the storage slot with the provided name, if it exists, `None`
156    /// otherwise.
157    pub fn get(&self, slot_name: &StorageSlotName) -> Option<&StorageSlot> {
158        self.slots.iter().find(|slot| slot.name().id() == slot_name.id())
159    }
160
161    /// Returns a mutable reference to the storage slot with the provided name, if it exists, `None`
162    /// otherwise.
163    fn get_mut(&mut self, slot_name: &StorageSlotName) -> Option<&mut StorageSlot> {
164        self.slots.iter_mut().find(|slot| slot.name().id() == slot_name.id())
165    }
166
167    /// Returns an item from the storage slot with the given name.
168    ///
169    /// # Errors
170    ///
171    /// Returns an error if:
172    /// - A slot with the provided name does not exist.
173    pub fn get_item(&self, slot_name: &StorageSlotName) -> Result<Word, AccountError> {
174        self.get(slot_name)
175            .map(|slot| slot.content().value())
176            .ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() })
177    }
178
179    /// Returns a map item from the map in the storage slot with the given name.
180    ///
181    /// # Errors
182    ///
183    /// Returns an error if:
184    /// - A slot with the provided name does not exist.
185    /// - If the [`StorageSlot`] is not [`StorageSlotType::Map`].
186    pub fn get_map_item(
187        &self,
188        slot_name: &StorageSlotName,
189        key: Word,
190    ) -> Result<Word, AccountError> {
191        self.get(slot_name)
192            .ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() })
193            .and_then(|slot| match slot.content() {
194                StorageSlotContent::Map(map) => Ok(map.get(&StorageMapKey::from_raw(key))),
195                _ => Err(AccountError::StorageSlotNotMap(slot_name.clone())),
196            })
197    }
198
199    // STATE MUTATORS
200    // --------------------------------------------------------------------------------------------
201
202    /// Applies the provided delta to this account storage.
203    ///
204    /// # Errors
205    ///
206    /// Returns an error if:
207    /// - The updates violate storage constraints.
208    pub(super) fn apply_delta(&mut self, delta: &AccountStorageDelta) -> Result<(), AccountError> {
209        // Update storage values
210        for (slot_name, &value) in delta.values() {
211            self.set_item(slot_name, value)?;
212        }
213
214        // Update storage maps
215        for (slot_name, map_delta) in delta.maps() {
216            let slot = self
217                .get_mut(slot_name)
218                .ok_or(AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() })?;
219
220            let storage_map = match slot.content_mut() {
221                StorageSlotContent::Map(map) => map,
222                _ => return Err(AccountError::StorageSlotNotMap(slot_name.clone())),
223            };
224
225            storage_map.apply_delta(map_delta)?;
226        }
227
228        Ok(())
229    }
230
231    /// Updates the value of the storage slot with the given name.
232    ///
233    /// This method should be used only to update value slots. For updating values
234    /// in storage maps, please see [`AccountStorage::set_map_item`].
235    ///
236    /// # Errors
237    ///
238    /// Returns an error if:
239    /// - A slot with the provided name does not exist.
240    /// - The [`StorageSlot`] is not [`StorageSlotType::Value`].
241    pub fn set_item(
242        &mut self,
243        slot_name: &StorageSlotName,
244        value: Word,
245    ) -> Result<Word, AccountError> {
246        let slot = self.get_mut(slot_name).ok_or_else(|| {
247            AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }
248        })?;
249
250        let StorageSlotContent::Value(old_value) = slot.content() else {
251            return Err(AccountError::StorageSlotNotValue(slot_name.clone()));
252        };
253        let old_value = *old_value;
254
255        let mut new_slot = StorageSlotContent::Value(value);
256        core::mem::swap(slot.content_mut(), &mut new_slot);
257
258        Ok(old_value)
259    }
260
261    /// Updates the value of a key-value pair of a storage map with the given name.
262    ///
263    /// This method should be used only to update storage maps. For updating values
264    /// in storage slots, please see [AccountStorage::set_item()].
265    ///
266    /// # Errors
267    ///
268    /// Returns an error if:
269    /// - A slot with the provided name does not exist.
270    /// - If the [`StorageSlot`] is not [`StorageSlotType::Map`].
271    pub fn set_map_item(
272        &mut self,
273        slot_name: &StorageSlotName,
274        key: StorageMapKey,
275        value: Word,
276    ) -> Result<(Word, Word), AccountError> {
277        let slot = self.get_mut(slot_name).ok_or_else(|| {
278            AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }
279        })?;
280
281        let StorageSlotContent::Map(storage_map) = slot.content_mut() else {
282            return Err(AccountError::StorageSlotNotMap(slot_name.clone()));
283        };
284
285        let old_root = storage_map.root();
286
287        let old_value = storage_map.insert(key, value)?;
288
289        Ok((old_root, old_value))
290    }
291}
292
293// ITERATORS
294// ================================================================================================
295
296impl IntoIterator for AccountStorage {
297    type Item = StorageSlot;
298    type IntoIter = alloc::vec::IntoIter<StorageSlot>;
299
300    fn into_iter(self) -> Self::IntoIter {
301        self.slots.into_iter()
302    }
303}
304
305// SEQUENTIAL COMMIT
306// ================================================================================================
307
308impl SequentialCommit for AccountStorage {
309    type Commitment = Word;
310
311    fn to_elements(&self) -> Vec<Felt> {
312        self.slots()
313            .iter()
314            .flat_map(|slot| {
315                StorageSlotHeader::new(
316                    slot.name().clone(),
317                    slot.content().slot_type(),
318                    slot.content().value(),
319                )
320                .to_elements()
321            })
322            .collect()
323    }
324}
325
326// SERIALIZATION
327// ================================================================================================
328
329impl Serializable for AccountStorage {
330    fn write_into<W: ByteWriter>(&self, target: &mut W) {
331        target.write_u8(self.slots().len() as u8);
332        target.write_many(self.slots());
333    }
334
335    fn get_size_hint(&self) -> usize {
336        // Size of the serialized slot length.
337        let u8_size = 0u8.get_size_hint();
338        let mut size = u8_size;
339
340        for slot in self.slots() {
341            size += slot.get_size_hint();
342        }
343
344        size
345    }
346}
347
348impl Deserializable for AccountStorage {
349    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
350        let num_slots = source.read_u8()? as usize;
351        let slots = source.read_many::<StorageSlot>(num_slots)?;
352
353        Self::new(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
354    }
355}
356
357// TESTS
358// ================================================================================================
359
360#[cfg(test)]
361mod tests {
362    use assert_matches::assert_matches;
363
364    use super::{AccountStorage, Deserializable, Serializable};
365    use crate::account::{AccountStorageHeader, StorageSlot, StorageSlotHeader, StorageSlotName};
366    use crate::errors::AccountError;
367
368    #[test]
369    fn test_serde_account_storage() -> anyhow::Result<()> {
370        // empty storage
371        let storage = AccountStorage::new(vec![]).unwrap();
372        let bytes = storage.to_bytes();
373        assert_eq!(storage, AccountStorage::read_from_bytes(&bytes).unwrap());
374
375        // storage with values for default types
376        let storage = AccountStorage::new(vec![
377            StorageSlot::with_empty_value(StorageSlotName::new("miden::test::value")?),
378            StorageSlot::with_empty_map(StorageSlotName::new("miden::test::map")?),
379        ])
380        .unwrap();
381        let bytes = storage.to_bytes();
382        assert_eq!(storage, AccountStorage::read_from_bytes(&bytes).unwrap());
383
384        Ok(())
385    }
386
387    #[test]
388    fn test_get_slot_by_name() -> anyhow::Result<()> {
389        let counter_slot = StorageSlotName::new("miden::test::counter")?;
390        let map_slot = StorageSlotName::new("miden::test::map")?;
391
392        let slots = vec![
393            StorageSlot::with_empty_value(counter_slot.clone()),
394            StorageSlot::with_empty_map(map_slot.clone()),
395        ];
396        let storage = AccountStorage::new(slots.clone())?;
397
398        assert_eq!(storage.get(&counter_slot).unwrap(), &slots[0]);
399        assert_eq!(storage.get(&map_slot).unwrap(), &slots[1]);
400
401        Ok(())
402    }
403
404    #[test]
405    fn test_account_storage_and_header_fail_on_duplicate_slot_name() -> anyhow::Result<()> {
406        let slot_name0 = StorageSlotName::mock(0);
407        let slot_name1 = StorageSlotName::mock(1);
408        let slot_name2 = StorageSlotName::mock(2);
409
410        let mut slots = vec![
411            StorageSlot::with_empty_value(slot_name0.clone()),
412            StorageSlot::with_empty_value(slot_name1.clone()),
413            StorageSlot::with_empty_map(slot_name0.clone()),
414            StorageSlot::with_empty_value(slot_name2.clone()),
415        ];
416
417        // Set up a test where the slots we pass are not already sorted
418        // This ensures the duplicate is correctly found
419        let err = AccountStorage::new(slots.clone()).unwrap_err();
420
421        assert_matches!(err, AccountError::DuplicateStorageSlotName(name) => {
422            assert_eq!(name, slot_name0);
423        });
424
425        slots.sort_unstable();
426        let err = AccountStorageHeader::new(slots.iter().map(StorageSlotHeader::from).collect())
427            .unwrap_err();
428
429        assert_matches!(err, AccountError::DuplicateStorageSlotName(name) => {
430            assert_eq!(name, slot_name0);
431        });
432
433        Ok(())
434    }
435}