miden_objects/account/storage/map/mod.rs
1use alloc::collections::BTreeMap;
2
3use miden_core::EMPTY_WORD;
4use miden_crypto::merkle::EmptySubtreeRoots;
5
6use super::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, Word};
7use crate::Hasher;
8use crate::account::StorageMapDelta;
9use crate::crypto::merkle::{InnerNodeInfo, LeafIndex, SMT_DEPTH, Smt, SmtLeaf, SmtProof};
10use crate::errors::StorageMapError;
11
12mod partial;
13pub use partial::PartialStorageMap;
14
15// ACCOUNT STORAGE MAP
16// ================================================================================================
17
18/// Empty storage map root.
19pub const EMPTY_STORAGE_MAP_ROOT: Word = *EmptySubtreeRoots::entry(StorageMap::TREE_DEPTH, 0);
20
21/// An account storage map is a sparse merkle tree of depth [`Self::TREE_DEPTH`] (64).
22///
23/// It can be used to store a large amount of data in an account than would be otherwise possible
24/// using just the account's storage slots. This works by storing the root of the map's underlying
25/// SMT in one account storage slot. Each map entry is a leaf in the tree and its inclusion is
26/// proven while retrieving it (e.g. via `account::get_map_item`).
27///
28/// As a side-effect, this also means that _not all_ entries of the map have to be present at
29/// transaction execution time in order to access or modify the map. It is sufficient if _just_ the
30/// accessed/modified items are present in the advice provider.
31///
32/// Because the keys of the map are user-chosen and thus not necessarily uniformly distributed, the
33/// tree could be imbalanced and made less efficient. To mitigate that, the keys used in the
34/// storage map are hashed before they are inserted into the SMT, which creates a uniform
35/// distribution. The original keys are retained in a separate map. This causes redundancy but
36/// allows for introspection of the map, e.g. by querying the set of stored (original) keys which is
37/// useful in debugging and explorer scenarios.
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct StorageMap {
40    /// The SMT where each key is the hashed original key.
41    smt: Smt,
42    /// The entries of the map where the key is the original user-chosen one.
43    ///
44    /// It is an invariant of this type that the map's entries are always consistent with the SMT's
45    /// entries and vice-versa.
46    map: BTreeMap<Word, Word>,
47}
48
49impl StorageMap {
50    // CONSTANTS
51    // --------------------------------------------------------------------------------------------
52
53    /// Depth of the storage tree.
54    pub const TREE_DEPTH: u8 = SMT_DEPTH;
55
56    /// The default value of empty leaves.
57    pub const EMPTY_VALUE: Word = Smt::EMPTY_VALUE;
58
59    // CONSTRUCTOR
60    // --------------------------------------------------------------------------------------------
61
62    /// Returns a new [StorageMap].
63    ///
64    /// All leaves in the returned tree are set to [Self::EMPTY_VALUE].
65    pub fn new() -> Self {
66        StorageMap { smt: Smt::new(), map: BTreeMap::new() }
67    }
68
69    /// Creates a new [`StorageMap`] from the provided key-value entries.
70    ///
71    /// # Errors
72    ///
73    /// Returns an error if:
74    /// - the provided entries contain multiple values for the same key.
75    pub fn with_entries<I: ExactSizeIterator<Item = (Word, Word)>>(
76        entries: impl IntoIterator<Item = (Word, Word), IntoIter = I>,
77    ) -> Result<Self, StorageMapError> {
78        let mut map = BTreeMap::new();
79
80        for (key, value) in entries {
81            if let Some(prev_value) = map.insert(key, value) {
82                return Err(StorageMapError::DuplicateKey {
83                    key,
84                    value0: prev_value,
85                    value1: value,
86                });
87            }
88        }
89
90        Ok(Self::from_btree_map(map))
91    }
92
93    /// Creates a new [`StorageMap`] from the given map. For internal use.
94    fn from_btree_map(map: BTreeMap<Word, Word>) -> Self {
95        let hashed_keys_iter = map.iter().map(|(key, value)| (Self::hash_key(*key), *value));
96        let smt = Smt::with_entries(hashed_keys_iter)
97            .expect("btree maps should not contain duplicate keys");
98
99        StorageMap { smt, map }
100    }
101
102    // PUBLIC ACCESSORS
103    // --------------------------------------------------------------------------------------------
104
105    /// Returns the root of the underlying sparse merkle tree.
106    pub fn root(&self) -> Word {
107        self.smt.root()
108    }
109
110    /// Returns the value corresponding to the key or [`Self::EMPTY_VALUE`] if the key is not
111    /// associated with a value.
112    pub fn get(&self, key: &Word) -> Word {
113        self.map.get(key).copied().unwrap_or_default()
114    }
115
116    /// Returns an opening of the leaf associated with `key`.
117    ///
118    /// Conceptually, an opening is a Merkle path to the leaf, as well as the leaf itself.
119    pub fn open(&self, key: &Word) -> SmtProof {
120        let key = Self::hash_key(*key);
121        self.smt.open(&key)
122    }
123
124    // ITERATORS
125    // --------------------------------------------------------------------------------------------
126
127    /// Returns an iterator over the leaves of the underlying [`Smt`].
128    pub fn leaves(&self) -> impl Iterator<Item = (LeafIndex<SMT_DEPTH>, &SmtLeaf)> {
129        self.smt.leaves() // Delegate to Smt's leaves method
130    }
131
132    /// Returns an iterator over the key value pairs of the map.
133    pub fn entries(&self) -> impl Iterator<Item = (&Word, &Word)> {
134        self.map.iter()
135    }
136
137    /// Returns an iterator over the inner nodes of the underlying [`Smt`].
138    pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
139        self.smt.inner_nodes() // Delegate to Smt's inner_nodes method
140    }
141
142    // DATA MUTATORS
143    // --------------------------------------------------------------------------------------------
144
145    /// Inserts or updates the given key value pair and returns the previous value, or
146    /// [`Self::EMPTY_VALUE`] if no entry was previously present.
147    ///
148    /// If the provided `value` is [`Self::EMPTY_VALUE`] the entry will be removed.
149    pub fn insert(&mut self, key: Word, value: Word) -> Word {
150        if value == EMPTY_WORD {
151            self.map.remove(&key);
152        } else {
153            self.map.insert(key, value);
154        }
155
156        let key = Self::hash_key(key);
157        self.smt.insert(key, value) // Delegate to Smt's insert method
158    }
159
160    /// Applies the provided delta to this account storage.
161    pub fn apply_delta(&mut self, delta: &StorageMapDelta) -> Word {
162        // apply the updated and cleared leaves to the storage map
163        for (&key, &value) in delta.entries().iter() {
164            self.insert(key.into_inner(), value);
165        }
166
167        self.root()
168    }
169
170    /// Consumes the map and returns the underlying map of entries.
171    pub fn into_entries(self) -> BTreeMap<Word, Word> {
172        self.map
173    }
174
175    /// Hashes the given key to get the key of the SMT.
176    pub fn hash_key(key: Word) -> Word {
177        Hasher::hash_elements(key.as_elements())
178    }
179}
180
181impl Default for StorageMap {
182    fn default() -> Self {
183        Self::new()
184    }
185}
186
187// SERIALIZATION
188// ================================================================================================
189
190impl Serializable for StorageMap {
191    fn write_into<W: ByteWriter>(&self, target: &mut W) {
192        self.map.write_into(target);
193    }
194
195    fn get_size_hint(&self) -> usize {
196        self.smt.get_size_hint()
197    }
198}
199
200impl Deserializable for StorageMap {
201    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
202        let map = BTreeMap::read_from(source)?;
203        Ok(Self::from_btree_map(map))
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use assert_matches::assert_matches;
210
211    use super::{Deserializable, EMPTY_STORAGE_MAP_ROOT, Serializable, StorageMap, Word};
212    use crate::errors::StorageMapError;
213
214    #[test]
215    fn account_storage_serialization() {
216        // StorageMap for default types (empty map)
217        let storage_map_default = StorageMap::default();
218        let bytes = storage_map_default.to_bytes();
219        assert_eq!(storage_map_default, StorageMap::read_from_bytes(&bytes).unwrap());
220
221        // StorageMap with values
222        let storage_map_leaves_2: [(Word, Word); 2] = [
223            (Word::from([101, 102, 103, 104u32]), Word::from([1, 2, 3, 4u32])),
224            (Word::from([105, 106, 107, 108u32]), Word::from([5, 6, 7, 8u32])),
225        ];
226        let storage_map = StorageMap::with_entries(storage_map_leaves_2).unwrap();
227
228        let bytes = storage_map.to_bytes();
229        let deserialized_map = StorageMap::read_from_bytes(&bytes).unwrap();
230
231        assert_eq!(storage_map.root(), deserialized_map.root());
232
233        assert_eq!(storage_map, deserialized_map);
234    }
235
236    #[test]
237    fn test_empty_storage_map_constants() {
238        // If these values don't match, update the constants.
239        assert_eq!(StorageMap::default().root(), EMPTY_STORAGE_MAP_ROOT);
240    }
241
242    #[test]
243    fn account_storage_map_fails_on_duplicate_entries() {
244        // StorageMap with values
245        let storage_map_leaves_2: [(Word, Word); 2] = [
246            (Word::from([101, 102, 103, 104u32]), Word::from([1, 2, 3, 4u32])),
247            (Word::from([101, 102, 103, 104u32]), Word::from([5, 6, 7, 8u32])),
248        ];
249
250        let error = StorageMap::with_entries(storage_map_leaves_2).unwrap_err();
251        assert_matches!(error, StorageMapError::DuplicateKey { .. });
252    }
253}