Skip to main content

miden_protocol/account/storage/map/
mod.rs

1use alloc::collections::BTreeMap;
2use alloc::string::ToString;
3
4use miden_crypto::merkle::EmptySubtreeRoots;
5
6use super::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, Word};
7use crate::account::StorageMapPatchEntries;
8use crate::crypto::merkle::smt::{LeafIndex, SMT_DEPTH, Smt, SmtLeaf};
9use crate::crypto::merkle::{InnerNodeInfo, MerkleError};
10use crate::errors::{AccountError, StorageMapError};
11
12mod key;
13pub use key::{StorageMapKey, StorageMapKeyHash};
14
15mod partial;
16pub use partial::PartialStorageMap;
17
18mod witness;
19pub use witness::StorageMapWitness;
20
21// ACCOUNT STORAGE MAP
22// ================================================================================================
23
24/// Empty storage map root.
25pub const EMPTY_STORAGE_MAP_ROOT: Word = *EmptySubtreeRoots::entry(StorageMap::DEPTH, 0);
26
27/// An account storage map is a sparse merkle tree of depth [`Self::DEPTH`].
28///
29/// It can be used to store a large amount of data in an account than would be otherwise possible
30/// using just the account's storage slots. This works by storing the root of the map's underlying
31/// SMT in one account storage slot. Each map entry is a leaf in the tree and its inclusion is
32/// proven while retrieving it (e.g. via `active_account::get_map_item`).
33///
34/// As a side-effect, this also means that _not all_ entries of the map have to be present at
35/// transaction execution time in order to access or modify the map. It is sufficient if _just_ the
36/// accessed/modified items are present in the advice provider.
37///
38/// Because the keys of the map are user-chosen and thus not necessarily uniformly distributed, the
39/// tree could be imbalanced and made less efficient. To mitigate that, the keys used in the
40/// storage map are hashed before they are inserted into the SMT, which creates a uniform
41/// distribution. The original keys are retained in a separate map. This causes redundancy but
42/// allows for introspection of the map, e.g. by querying the set of stored (original) keys which is
43/// useful in debugging and explorer scenarios.
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct StorageMap {
46    /// The SMT where each key is the hashed original key.
47    smt: Smt,
48    /// The entries of the map that retains the original unhashed keys (i.e. [`StorageMapKey`]).
49    ///
50    /// It is an invariant of this type that the map's entries are always consistent with the SMT's
51    /// entries and vice-versa.
52    entries: BTreeMap<StorageMapKey, Word>,
53}
54
55impl StorageMap {
56    // CONSTANTS
57    // --------------------------------------------------------------------------------------------
58
59    /// The depth of the SMT that represents the storage map.
60    pub const DEPTH: u8 = SMT_DEPTH;
61
62    /// The default value of empty leaves.
63    pub const EMPTY_VALUE: Word = Smt::EMPTY_VALUE;
64
65    // CONSTRUCTOR
66    // --------------------------------------------------------------------------------------------
67
68    /// Returns a new [StorageMap].
69    ///
70    /// All leaves in the returned tree are set to [Self::EMPTY_VALUE].
71    pub fn new() -> Self {
72        StorageMap {
73            smt: Smt::new(),
74            entries: BTreeMap::new(),
75        }
76    }
77
78    /// Creates a new [`StorageMap`] from the provided key-value entries.
79    ///
80    /// An entry whose value is [`Self::EMPTY_VALUE`] is dropped, matching [`Self::insert`].
81    ///
82    /// # Errors
83    ///
84    /// Returns an error if:
85    /// - the provided entries contain multiple values for the same key.
86    /// - a single tree leaf would hold more entries than the tree allows.
87    pub fn with_entries<I: ExactSizeIterator<Item = (StorageMapKey, Word)>>(
88        entries: impl IntoIterator<Item = (StorageMapKey, Word), IntoIter = I>,
89    ) -> Result<Self, StorageMapError> {
90        let mut map = BTreeMap::new();
91
92        for (key, value) in entries {
93            if let Some(prev_value) = map.insert(key, value) {
94                return Err(StorageMapError::DuplicateKey {
95                    key,
96                    value0: prev_value,
97                    value1: value,
98                });
99            }
100        }
101
102        Self::from_btree_map(map).map_err(StorageMapError::MaxLeafEntriesExceeded)
103    }
104
105    /// Creates a new [`StorageMap`] from the given map of unique keys. For internal use.
106    ///
107    /// Empty values are dropped because the SMT treats them as absent, and this type's entries
108    /// must stay consistent with it.
109    ///
110    /// # Errors
111    ///
112    /// Returns an error if a single tree leaf would hold more entries than the tree allows.
113    pub(crate) fn from_btree_map(
114        mut entries: BTreeMap<StorageMapKey, Word>,
115    ) -> Result<Self, MerkleError> {
116        entries.retain(|_, value| *value != Self::EMPTY_VALUE);
117
118        let hashed_keys_iter = entries.iter().map(|(key, value)| (key.hash().as_word(), *value));
119        // The keys are unique, so the only remaining failure is an overfull leaf.
120        let smt = Smt::with_entries(hashed_keys_iter)?;
121
122        Ok(StorageMap { smt, entries })
123    }
124
125    // PUBLIC ACCESSORS
126    // --------------------------------------------------------------------------------------------
127
128    /// Returns the root of the underlying sparse merkle tree.
129    pub fn root(&self) -> Word {
130        self.smt.root()
131    }
132
133    /// Returns the number of non-empty leaves in this storage map.
134    ///
135    /// Note that this may return a different value from [Self::num_entries()] as a single leaf may
136    /// contain more than one key-value pair.
137    pub fn num_leaves(&self) -> usize {
138        self.smt.num_leaves()
139    }
140
141    /// Returns the number of key-value pairs with non-default values in this storage map.
142    ///
143    /// Note that this may return a different value from [Self::num_leaves()] as a single leaf may
144    /// contain more than one key-value pair.
145    pub fn num_entries(&self) -> usize {
146        self.smt.num_entries()
147    }
148
149    /// Returns the value corresponding to the key or [`Self::EMPTY_VALUE`] if the key is not
150    /// associated with a value.
151    pub fn get(&self, key: &StorageMapKey) -> Word {
152        self.entries.get(key).copied().unwrap_or_default()
153    }
154
155    /// Returns an opening of the leaf associated with the given key.
156    ///
157    /// Conceptually, an opening is a Merkle path to the leaf, as well as the leaf itself.
158    pub fn open(&self, key: &StorageMapKey) -> StorageMapWitness {
159        let smt_proof = self.smt.open(&key.hash().as_word());
160        let value = self.entries.get(key).copied().unwrap_or_default();
161
162        // SAFETY: The key value pair is guaranteed to be present in the provided proof since we
163        // open its hashed version and because of the guarantees of the storage map.
164        StorageMapWitness::new_unchecked(smt_proof, [(*key, value)])
165    }
166
167    // ITERATORS
168    // --------------------------------------------------------------------------------------------
169
170    /// Returns an iterator over the leaves of the underlying [`Smt`].
171    pub fn leaves(&self) -> impl Iterator<Item = (LeafIndex<SMT_DEPTH>, &SmtLeaf)> {
172        self.smt.leaves() // Delegate to Smt's leaves method
173    }
174
175    /// Returns an iterator over the key-value pairs in this storage map.
176    pub fn entries(&self) -> impl Iterator<Item = (&StorageMapKey, &Word)> {
177        self.entries.iter()
178    }
179
180    /// Returns an iterator over the inner nodes of the underlying [`Smt`].
181    pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
182        self.smt.inner_nodes() // Delegate to Smt's inner_nodes method
183    }
184
185    // DATA MUTATORS
186    // --------------------------------------------------------------------------------------------
187
188    /// Inserts or updates the given key value pair and returns the previous value, or
189    /// [`Self::EMPTY_VALUE`] if no entry was previously present.
190    ///
191    /// If the provided `value` is [`Self::EMPTY_VALUE`] the entry will be removed.
192    pub fn insert(&mut self, key: StorageMapKey, value: Word) -> Result<Word, AccountError> {
193        // Update the tree first to not leave the map in an inconsistent state if it fails.
194        let previous = self
195            .smt
196            .insert(key.hash().into(), value)
197            .map_err(AccountError::MaxNumStorageMapLeavesExceeded)?;
198
199        if value == Self::EMPTY_VALUE {
200            self.entries.remove(&key);
201        } else {
202            self.entries.insert(key, value);
203        }
204
205        Ok(previous)
206    }
207
208    /// Applies the provided map patch entries to this storage map.
209    pub fn apply_patch(&mut self, entries: &StorageMapPatchEntries) -> Result<Word, AccountError> {
210        // apply the updated and cleared leaves to the storage map
211        for (&key, &value) in entries.as_map().iter() {
212            self.insert(key, value)?;
213        }
214
215        Ok(self.root())
216    }
217
218    /// Consumes the map and returns the underlying map of entries.
219    pub fn into_entries(self) -> BTreeMap<StorageMapKey, Word> {
220        self.entries
221    }
222}
223
224impl Default for StorageMap {
225    fn default() -> Self {
226        Self::new()
227    }
228}
229
230// SERIALIZATION
231// ================================================================================================
232
233impl Serializable for StorageMap {
234    fn write_into<W: ByteWriter>(&self, target: &mut W) {
235        self.entries.write_into(target);
236    }
237
238    fn get_size_hint(&self) -> usize {
239        self.smt.get_size_hint()
240    }
241}
242
243impl Deserializable for StorageMap {
244    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
245        let map = BTreeMap::read_from(source)?;
246        Self::from_btree_map(map)
247            .map_err(|error| DeserializationError::InvalidValue(error.to_string()))
248    }
249}
250
251#[cfg(test)]
252mod tests {
253    use assert_matches::assert_matches;
254
255    use super::{
256        Deserializable,
257        EMPTY_STORAGE_MAP_ROOT,
258        Serializable,
259        StorageMap,
260        StorageMapKey,
261        Word,
262    };
263    use crate::errors::StorageMapError;
264
265    #[test]
266    fn account_storage_serialization() {
267        // StorageMap for default types (empty map)
268        let storage_map_default = StorageMap::default();
269        let bytes = storage_map_default.to_bytes();
270        assert_eq!(storage_map_default, StorageMap::read_from_bytes(&bytes).unwrap());
271
272        // StorageMap with values
273        let storage_map_leaves_2 = [
274            (StorageMapKey::from_array([101, 102, 103, 104]), Word::from([1, 2, 3, 4u32])),
275            (StorageMapKey::from_array([105, 106, 107, 108]), Word::from([5, 6, 7, 8u32])),
276        ];
277        let storage_map = StorageMap::with_entries(storage_map_leaves_2).unwrap();
278        assert_eq!(storage_map.num_entries(), 2);
279        assert_eq!(storage_map.num_leaves(), 2);
280
281        let bytes = storage_map.to_bytes();
282        let deserialized_map = StorageMap::read_from_bytes(&bytes).unwrap();
283
284        assert_eq!(storage_map.root(), deserialized_map.root());
285
286        assert_eq!(storage_map, deserialized_map);
287    }
288
289    #[test]
290    fn test_empty_storage_map_constants() {
291        // If these values don't match, update the constants.
292        assert_eq!(StorageMap::default().root(), EMPTY_STORAGE_MAP_ROOT);
293    }
294
295    #[test]
296    fn account_storage_map_fails_on_duplicate_entries() {
297        // StorageMap with values
298        let storage_map_leaves_2 = [
299            (StorageMapKey::from_array([101, 102, 103, 104]), Word::from([1, 2, 3, 4u32])),
300            (StorageMapKey::from_array([101, 102, 103, 104]), Word::from([5, 6, 7, 8u32])),
301        ];
302
303        let error = StorageMap::with_entries(storage_map_leaves_2).unwrap_err();
304        assert_matches!(error, StorageMapError::DuplicateKey { .. });
305    }
306
307    /// An empty value is absent from the tree, so the entries must not keep it either.
308    #[test]
309    fn storage_map_drops_entries_with_an_empty_value() -> anyhow::Result<()> {
310        let empty_key = StorageMapKey::from_array([101, 102, 103, 104]);
311        let present_key = StorageMapKey::from_array([105, 106, 107, 108]);
312        let value = Word::from([5, 6, 7, 8u32]);
313
314        let map =
315            StorageMap::with_entries([(empty_key, StorageMap::EMPTY_VALUE), (present_key, value)])?;
316
317        assert_eq!(map.num_entries(), 1);
318        assert_eq!(map.entries().count(), 1);
319        assert_eq!(map.get(&empty_key), StorageMap::EMPTY_VALUE);
320        assert_eq!(map, StorageMap::with_entries([(present_key, value)])?);
321
322        Ok(())
323    }
324}