miden_protocol/account/storage/map/partial.rs
1use 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/// A partial representation of a [`StorageMap`], containing only proofs for a subset of the
17/// key-value pairs.
18///
19/// A partial storage map carries only the Merkle authentication data a transaction will need.
20/// Every included entry pairs a value with its proof, letting the transaction kernel verify reads
21/// (and prepare writes) without needing the complete tree.
22///
23/// ## Guarantees
24///
25/// This type guarantees that the raw key-value pairs it contains are all present in the
26/// contained partial SMT. Note that the inverse is not necessarily true. The SMT may contain more
27/// entries than the map because to prove inclusion of a given raw key A an
28/// [`SmtLeaf::Multiple`] may be present that contains both keys hash(A) and hash(B). However, B may
29/// not be present in the key-value pairs and this is a valid state.
30#[derive(Clone, Debug, PartialEq, Eq, Default)]
31pub struct PartialStorageMap {
32 partial_smt: PartialSmt,
33 /// The entries of the map that retains the original unhashed keys (i.e. [`StorageMapKey`]).
34 ///
35 /// It is an invariant of this type that the map's entries are always consistent with the
36 /// partial SMT's entries and vice-versa.
37 entries: BTreeMap<StorageMapKey, Word>,
38}
39
40impl PartialStorageMap {
41 // CONSTRUCTORS
42 // --------------------------------------------------------------------------------------------
43
44 /// Constructs a [`PartialStorageMap`] from a [`StorageMap`] root.
45 ///
46 /// For conversion from a [`StorageMap`], prefer [`Self::new_minimal`] to be more explicit.
47 pub fn new(root: Word) -> Self {
48 PartialStorageMap {
49 partial_smt: PartialSmt::new(root),
50 entries: BTreeMap::new(),
51 }
52 }
53
54 /// Returns a new instance of a [`PartialStorageMap`] with all provided witnesses added to it.
55 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 /// Converts a [`StorageMap`] into a partial storage representation.
69 ///
70 /// The resulting [`PartialStorageMap`] will contain the _full_ entries and merkle paths of the
71 /// original storage map.
72 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 /// Converts a [`StorageMap`] into a partial storage representation.
80 ///
81 /// The resulting [`PartialStorageMap`] will represent the root of the storage map, but not
82 /// track any key-value pairs, which means it is the most _minimal_ representation of the
83 /// storage map.
84 pub fn new_minimal(storage_map: &StorageMap) -> Self {
85 Self::new(storage_map.root())
86 }
87
88 // ACCESSORS
89 // --------------------------------------------------------------------------------------------
90
91 /// Returns a reference to the underlying [`PartialSmt`].
92 pub fn partial_smt(&self) -> &PartialSmt {
93 &self.partial_smt
94 }
95
96 /// Returns the root of the underlying [`PartialSmt`].
97 pub fn root(&self) -> Word {
98 self.partial_smt.root()
99 }
100
101 /// Looks up the provided key in this map and returns:
102 /// - a non-empty [`Word`] if the key is tracked by this map and exists in it,
103 /// - [`Word::empty`] if the key is tracked by this map and does not exist,
104 /// - `None` if the key is not tracked by this map.
105 pub fn get(&self, key: &StorageMapKey) -> Option<Word> {
106 let hash_word = key.hash().as_word();
107 // This returns an error if the key is not tracked which we map to a `None`.
108 self.partial_smt.get_value(&hash_word).ok()
109 }
110
111 /// Returns an opening of the leaf associated with the given key.
112 ///
113 /// Conceptually, an opening is a Merkle path to the leaf, as well as the leaf itself.
114 ///
115 /// # Errors
116 ///
117 /// Returns an error if:
118 /// - the key is not tracked by this partial storage map.
119 pub fn open(&self, key: &StorageMapKey) -> Result<StorageMapWitness, MerkleError> {
120 let smt_proof = self.partial_smt.open(&key.hash().as_word())?;
121 let value = self.entries.get(key).copied().unwrap_or_default();
122
123 // SAFETY: The key value pair is guaranteed to be present in the provided proof since we
124 // open its hashed version and because of the guarantees of the partial storage map.
125 Ok(StorageMapWitness::new_unchecked(smt_proof, [(*key, value)]))
126 }
127
128 // ITERATORS
129 // --------------------------------------------------------------------------------------------
130
131 /// Returns an iterator over the leaves of the underlying [`PartialSmt`].
132 pub fn leaves(&self) -> impl Iterator<Item = (LeafIndex<SMT_DEPTH>, &SmtLeaf)> {
133 self.partial_smt.leaves()
134 }
135
136 /// Returns an iterator over the key-value pairs in this storage map.
137 pub fn entries(&self) -> impl Iterator<Item = (&StorageMapKey, &Word)> {
138 self.entries.iter()
139 }
140
141 /// Returns an iterator over the inner nodes of the underlying [`PartialSmt`].
142 pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
143 self.partial_smt.inner_nodes()
144 }
145
146 // MUTATORS
147 // --------------------------------------------------------------------------------------------
148
149 /// Adds a [`StorageMapWitness`] for the specific key-value pair to this [`PartialStorageMap`].
150 pub fn add(&mut self, witness: StorageMapWitness) -> Result<(), MerkleError> {
151 self.entries.extend(witness.entries().map(|(key, value)| (*key, *value)));
152 self.partial_smt.add_proof(SmtProof::from(witness))
153 }
154}
155
156impl Serializable for PartialStorageMap {
157 fn write_into<W: ByteWriter>(&self, target: &mut W) {
158 target.write(&self.partial_smt);
159 target.write_usize(self.entries.len());
160 target.write_many(self.entries.keys());
161 }
162}
163
164impl Deserializable for PartialStorageMap {
165 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
166 let mut map = BTreeMap::new();
167
168 let partial_smt: PartialSmt = source.read()?;
169 let num_entries: usize = source.read()?;
170
171 for _ in 0..num_entries {
172 let key: StorageMapKey = source.read()?;
173 let hashed_map_key: Word = key.hash().into();
174 let value = partial_smt.get_value(&hashed_map_key).map_err(|err| {
175 DeserializationError::InvalidValue(format!(
176 "failed to find map key {key} in partial SMT: {err}"
177 ))
178 })?;
179 map.insert(key, value);
180 }
181
182 Ok(PartialStorageMap { partial_smt, entries: map })
183 }
184}