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