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