miden_protocol/account/storage/map/
partial.rs1use alloc::collections::BTreeMap;
2
3use miden_crypto::Word;
4use miden_crypto::merkle::smt::{LeafIndex, PartialSmt, SMT_DEPTH, SmtLeaf, SmtProof};
5use miden_crypto::merkle::{InnerNodeInfo, MerkleError};
6
7use crate::account::{StorageMap, StorageMapKey, StorageMapWitness};
8use crate::utils::serde::{
9 ByteReader,
10 ByteWriter,
11 Deserializable,
12 DeserializationError,
13 Serializable,
14};
15
16#[derive(Clone, Debug, PartialEq, Eq, Default)]
31pub struct PartialStorageMap {
32 partial_smt: PartialSmt,
33 entries: BTreeMap<StorageMapKey, Word>,
38}
39
40impl PartialStorageMap {
41 pub fn new(root: Word) -> Self {
48 PartialStorageMap {
49 partial_smt: PartialSmt::new(root),
50 entries: BTreeMap::new(),
51 }
52 }
53
54 pub fn with_witnesses(
56 witnesses: impl IntoIterator<Item = StorageMapWitness>,
57 ) -> Result<Self, MerkleError> {
58 let mut map = BTreeMap::new();
59
60 let partial_smt = PartialSmt::from_proofs(witnesses.into_iter().map(|witness| {
61 map.extend(witness.entries());
62 SmtProof::from(witness)
63 }))?;
64
65 Ok(PartialStorageMap { partial_smt, entries: map })
66 }
67
68 pub fn new_full(storage_map: StorageMap) -> Self {
73 let partial_smt = PartialSmt::from(storage_map.smt);
74 let entries = storage_map.entries;
75
76 PartialStorageMap { partial_smt, entries }
77 }
78
79 pub fn new_minimal(storage_map: &StorageMap) -> Self {
85 Self::new(storage_map.root())
86 }
87
88 pub fn try_from_parts(
97 partial_smt: PartialSmt,
98 keys: impl IntoIterator<Item = StorageMapKey>,
99 ) -> Result<Self, MerkleError> {
100 let mut entries = BTreeMap::new();
101
102 for key in keys {
103 if entries.contains_key(&key) {
104 return Err(MerkleError::DuplicateValuesForIndex(
105 key.hash().to_leaf_index().position(),
106 ));
107 }
108
109 let value = partial_smt.get_value(&key.hash().as_word())?;
110 entries.insert(key, value);
111 }
112
113 Ok(Self { partial_smt, entries })
114 }
115
116 pub fn partial_smt(&self) -> &PartialSmt {
121 &self.partial_smt
122 }
123
124 pub fn root(&self) -> Word {
126 self.partial_smt.root()
127 }
128
129 pub fn get(&self, key: &StorageMapKey) -> Option<Word> {
134 let hash_word = key.hash().as_word();
135 self.partial_smt.get_value(&hash_word).ok()
137 }
138
139 pub fn open(&self, key: &StorageMapKey) -> Result<StorageMapWitness, MerkleError> {
148 let smt_proof = self.partial_smt.open(&key.hash().as_word())?;
149 let value = self.entries.get(key).copied().unwrap_or_default();
150
151 Ok(StorageMapWitness::new_unchecked(smt_proof, [(*key, value)]))
154 }
155
156 pub fn leaves(&self) -> impl Iterator<Item = (LeafIndex<SMT_DEPTH>, &SmtLeaf)> {
161 self.partial_smt.leaves()
162 }
163
164 pub fn entries(&self) -> impl Iterator<Item = (&StorageMapKey, &Word)> {
166 self.entries.iter()
167 }
168
169 pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
171 self.partial_smt.inner_nodes()
172 }
173
174 pub fn add(&mut self, witness: StorageMapWitness) -> Result<(), MerkleError> {
179 self.entries.extend(witness.entries().map(|(key, value)| (*key, *value)));
180 self.partial_smt.add_proof(SmtProof::from(witness))
181 }
182}
183
184impl Serializable for PartialStorageMap {
185 fn write_into<W: ByteWriter>(&self, target: &mut W) {
186 target.write(&self.partial_smt);
187 target.write_usize(self.entries.len());
188 target.write_many(self.entries.keys());
189 }
190}
191
192impl Deserializable for PartialStorageMap {
193 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
194 let partial_smt: PartialSmt = source.read()?;
195 let num_entries: usize = source.read()?;
196 let keys = source
197 .read_many_iter::<StorageMapKey>(num_entries)?
198 .collect::<Result<alloc::vec::Vec<_>, _>>()?;
199
200 Self::try_from_parts(partial_smt, keys).map_err(|err| {
201 DeserializationError::InvalidValue(format!(
202 "failed to construct partial storage map from supplied keys: {err}"
203 ))
204 })
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use alloc::vec::Vec;
211
212 use assert_matches::assert_matches;
213 use miden_crypto::merkle::MerkleError;
214 use miden_crypto::merkle::smt::PartialSmt;
215
216 use super::PartialStorageMap;
217 use crate::Word;
218 use crate::account::{StorageMap, StorageMapKey};
219
220 #[test]
221 fn try_from_parts_preserves_unrelated_partial_smt_material() -> anyhow::Result<()> {
222 let tracked_key = StorageMapKey::from_index(1);
223 let extra_key = StorageMapKey::from_index(2);
224 let tracked_value = Word::from([1_u32, 0, 0, 0]);
225 let extra_value = Word::from([2_u32, 0, 0, 0]);
226 let storage_map =
227 StorageMap::with_entries([(tracked_key, tracked_value), (extra_key, extra_value)])?;
228 let partial_smt = PartialSmt::from_proofs([
229 storage_map.open(&tracked_key).into(),
230 storage_map.open(&extra_key).into(),
231 ])?;
232
233 let partial_map = PartialStorageMap::try_from_parts(partial_smt, [tracked_key])?;
234
235 assert_eq!(partial_map.entries().collect::<Vec<_>>(), [(&tracked_key, &tracked_value)]);
236 assert_eq!(partial_map.get(&extra_key), Some(extra_value));
237
238 Ok(())
239 }
240
241 #[test]
242 fn try_from_parts_rejects_duplicate_keys() -> anyhow::Result<()> {
243 let key = StorageMapKey::from_index(1);
244 let storage_map = StorageMap::with_entries([(key, Word::from([1_u32, 0, 0, 0]))])?;
245 let partial_smt = PartialSmt::from_proofs([storage_map.open(&key).into()])?;
246
247 let result = PartialStorageMap::try_from_parts(partial_smt, [key, key]);
248
249 assert_matches!(
250 result,
251 Err(MerkleError::DuplicateValuesForIndex(position))
252 if position == key.hash().to_leaf_index().position()
253 );
254
255 Ok(())
256 }
257
258 #[test]
259 fn try_from_parts_rejects_untracked_keys() {
260 let key = StorageMapKey::from_index(1);
261 let result = PartialStorageMap::try_from_parts(PartialSmt::new(Word::empty()), [key]);
262
263 assert_matches!(
264 result,
265 Err(MerkleError::UntrackedKey(hashed_key)) if hashed_key == key.hash().as_word()
266 );
267 }
268}