miden_protocol/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::account::StorageMapDelta;
8use crate::crypto::merkle::InnerNodeInfo;
9use crate::crypto::merkle::smt::{LeafIndex, SMT_DEPTH, Smt, SmtLeaf};
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 /// # Errors
81 ///
82 /// Returns an error if:
83 /// - the provided entries contain multiple values for the same key.
84 pub fn with_entries<I: ExactSizeIterator<Item = (StorageMapKey, Word)>>(
85 entries: impl IntoIterator<Item = (StorageMapKey, Word), IntoIter = I>,
86 ) -> Result<Self, StorageMapError> {
87 let mut map = BTreeMap::new();
88
89 for (key, value) in entries {
90 if let Some(prev_value) = map.insert(key, value) {
91 return Err(StorageMapError::DuplicateKey {
92 key,
93 value0: prev_value,
94 value1: value,
95 });
96 }
97 }
98
99 Ok(Self::from_btree_map(map))
100 }
101
102 /// Creates a new [`StorageMap`] from the given map. For internal use.
103 fn from_btree_map(entries: BTreeMap<StorageMapKey, Word>) -> Self {
104 let hashed_keys_iter = entries.iter().map(|(key, value)| (key.hash().as_word(), *value));
105 let smt = Smt::with_entries(hashed_keys_iter)
106 .expect("btree maps should not contain duplicate keys");
107
108 StorageMap { smt, entries }
109 }
110
111 // PUBLIC ACCESSORS
112 // --------------------------------------------------------------------------------------------
113
114 /// Returns the root of the underlying sparse merkle tree.
115 pub fn root(&self) -> Word {
116 self.smt.root()
117 }
118
119 /// Returns the number of non-empty leaves in this storage map.
120 ///
121 /// Note that this may return a different value from [Self::num_entries()] as a single leaf may
122 /// contain more than one key-value pair.
123 pub fn num_leaves(&self) -> usize {
124 self.smt.num_leaves()
125 }
126
127 /// Returns the number of key-value pairs with non-default values in this storage map.
128 ///
129 /// Note that this may return a different value from [Self::num_leaves()] as a single leaf may
130 /// contain more than one key-value pair.
131 pub fn num_entries(&self) -> usize {
132 self.smt.num_entries()
133 }
134
135 /// Returns the value corresponding to the key or [`Self::EMPTY_VALUE`] if the key is not
136 /// associated with a value.
137 pub fn get(&self, key: &StorageMapKey) -> Word {
138 self.entries.get(key).copied().unwrap_or_default()
139 }
140
141 /// Returns an opening of the leaf associated with the given key.
142 ///
143 /// Conceptually, an opening is a Merkle path to the leaf, as well as the leaf itself.
144 pub fn open(&self, key: &StorageMapKey) -> StorageMapWitness {
145 let smt_proof = self.smt.open(&key.hash().as_word());
146 let value = self.entries.get(key).copied().unwrap_or_default();
147
148 // SAFETY: The key value pair is guaranteed to be present in the provided proof since we
149 // open its hashed version and because of the guarantees of the storage map.
150 StorageMapWitness::new_unchecked(smt_proof, [(*key, value)])
151 }
152
153 // ITERATORS
154 // --------------------------------------------------------------------------------------------
155
156 /// Returns an iterator over the leaves of the underlying [`Smt`].
157 pub fn leaves(&self) -> impl Iterator<Item = (LeafIndex<SMT_DEPTH>, &SmtLeaf)> {
158 self.smt.leaves() // Delegate to Smt's leaves method
159 }
160
161 /// Returns an iterator over the key-value pairs in this storage map.
162 pub fn entries(&self) -> impl Iterator<Item = (&StorageMapKey, &Word)> {
163 self.entries.iter()
164 }
165
166 /// Returns an iterator over the inner nodes of the underlying [`Smt`].
167 pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
168 self.smt.inner_nodes() // Delegate to Smt's inner_nodes method
169 }
170
171 // DATA MUTATORS
172 // --------------------------------------------------------------------------------------------
173
174 /// Inserts or updates the given key value pair and returns the previous value, or
175 /// [`Self::EMPTY_VALUE`] if no entry was previously present.
176 ///
177 /// If the provided `value` is [`Self::EMPTY_VALUE`] the entry will be removed.
178 pub fn insert(&mut self, key: StorageMapKey, value: Word) -> Result<Word, AccountError> {
179 if value == EMPTY_WORD {
180 self.entries.remove(&key);
181 } else {
182 self.entries.insert(key, value);
183 }
184
185 let hashed_key = key.hash();
186 self.smt
187 .insert(hashed_key.into(), value)
188 .map_err(AccountError::MaxNumStorageMapLeavesExceeded)
189 }
190
191 /// Applies the provided delta to this account storage.
192 pub fn apply_delta(&mut self, delta: &StorageMapDelta) -> Result<Word, AccountError> {
193 // apply the updated and cleared leaves to the storage map
194 for (&key, &value) in delta.entries().iter() {
195 self.insert(key.into_inner(), value)?;
196 }
197
198 Ok(self.root())
199 }
200
201 /// Consumes the map and returns the underlying map of entries.
202 pub fn into_entries(self) -> BTreeMap<StorageMapKey, Word> {
203 self.entries
204 }
205}
206
207impl Default for StorageMap {
208 fn default() -> Self {
209 Self::new()
210 }
211}
212
213// SERIALIZATION
214// ================================================================================================
215
216impl Serializable for StorageMap {
217 fn write_into<W: ByteWriter>(&self, target: &mut W) {
218 self.entries.write_into(target);
219 }
220
221 fn get_size_hint(&self) -> usize {
222 self.smt.get_size_hint()
223 }
224}
225
226impl Deserializable for StorageMap {
227 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
228 let map = BTreeMap::read_from(source)?;
229 Ok(Self::from_btree_map(map))
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use assert_matches::assert_matches;
236
237 use super::{
238 Deserializable,
239 EMPTY_STORAGE_MAP_ROOT,
240 Serializable,
241 StorageMap,
242 StorageMapKey,
243 Word,
244 };
245 use crate::errors::StorageMapError;
246
247 #[test]
248 fn account_storage_serialization() {
249 // StorageMap for default types (empty map)
250 let storage_map_default = StorageMap::default();
251 let bytes = storage_map_default.to_bytes();
252 assert_eq!(storage_map_default, StorageMap::read_from_bytes(&bytes).unwrap());
253
254 // StorageMap with values
255 let storage_map_leaves_2 = [
256 (StorageMapKey::from_array([101, 102, 103, 104]), Word::from([1, 2, 3, 4u32])),
257 (StorageMapKey::from_array([105, 106, 107, 108]), Word::from([5, 6, 7, 8u32])),
258 ];
259 let storage_map = StorageMap::with_entries(storage_map_leaves_2).unwrap();
260 assert_eq!(storage_map.num_entries(), 2);
261 assert_eq!(storage_map.num_leaves(), 2);
262
263 let bytes = storage_map.to_bytes();
264 let deserialized_map = StorageMap::read_from_bytes(&bytes).unwrap();
265
266 assert_eq!(storage_map.root(), deserialized_map.root());
267
268 assert_eq!(storage_map, deserialized_map);
269 }
270
271 #[test]
272 fn test_empty_storage_map_constants() {
273 // If these values don't match, update the constants.
274 assert_eq!(StorageMap::default().root(), EMPTY_STORAGE_MAP_ROOT);
275 }
276
277 #[test]
278 fn account_storage_map_fails_on_duplicate_entries() {
279 // StorageMap with values
280 let storage_map_leaves_2 = [
281 (StorageMapKey::from_array([101, 102, 103, 104]), Word::from([1, 2, 3, 4u32])),
282 (StorageMapKey::from_array([101, 102, 103, 104]), Word::from([5, 6, 7, 8u32])),
283 ];
284
285 let error = StorageMap::with_entries(storage_map_leaves_2).unwrap_err();
286 assert_matches!(error, StorageMapError::DuplicateKey { .. });
287 }
288}