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