miden_protocol/account/storage/
partial.rs1use alloc::collections::{BTreeMap, BTreeSet};
2
3use miden_crypto::Word;
4use miden_crypto::merkle::InnerNodeInfo;
5use miden_crypto::merkle::smt::SmtLeaf;
6
7use super::{AccountStorage, AccountStorageHeader, StorageSlotContent};
8use crate::account::PartialStorageMap;
9use crate::errors::AccountError;
10use crate::utils::serde::{
11 ByteReader,
12 ByteWriter,
13 Deserializable,
14 DeserializationError,
15 Serializable,
16};
17
18#[derive(Clone, Debug, PartialEq, Eq)]
24pub struct PartialStorage {
25 commitment: Word,
27 header: AccountStorageHeader,
29 maps: BTreeMap<Word, PartialStorageMap>,
32}
33
34impl PartialStorage {
35 pub fn new(
44 storage_header: AccountStorageHeader,
45 storage_maps: impl IntoIterator<Item = PartialStorageMap>,
46 ) -> Result<Self, AccountError> {
47 let storage_map_roots: BTreeSet<_> = storage_header.map_slot_roots().collect();
48 let mut maps = BTreeMap::new();
49 for smt in storage_maps {
50 if !storage_map_roots.contains(&smt.root()) {
52 return Err(AccountError::StorageMapRootNotFound(smt.root()));
53 }
54 maps.insert(smt.root(), smt);
55 }
56
57 let commitment = storage_header.to_commitment();
58 Ok(Self { commitment, header: storage_header, maps })
59 }
60
61 pub fn new_full(account_storage: AccountStorage) -> Self {
66 let header: AccountStorageHeader = account_storage.to_header();
67 let commitment = header.to_commitment();
68
69 let mut maps = BTreeMap::new();
70 for slot in account_storage {
71 if let StorageSlotContent::Map(storage_map) = slot.into_parts().1 {
72 let partial_map = PartialStorageMap::new_full(storage_map);
73 maps.insert(partial_map.root(), partial_map);
74 }
75 }
76
77 PartialStorage { header, maps, commitment }
78 }
79
80 pub fn new_minimal(account_storage: &AccountStorage) -> Self {
85 let header: AccountStorageHeader = account_storage.to_header();
86 let commitment = header.to_commitment();
87
88 let mut maps = BTreeMap::new();
89 for slot in account_storage.slots() {
90 if let StorageSlotContent::Map(storage_map) = slot.content() {
91 let partial_map = PartialStorageMap::new_minimal(storage_map);
92 maps.insert(partial_map.root(), partial_map);
93 }
94 }
95
96 PartialStorage { header, maps, commitment }
97 }
98
99 pub fn header(&self) -> &AccountStorageHeader {
104 &self.header
105 }
106
107 pub fn commitment(&self) -> Word {
109 self.commitment
110 }
111
112 pub fn into_parts(self) -> (Word, AccountStorageHeader, BTreeMap<Word, PartialStorageMap>) {
116 (self.commitment, self.header, self.maps)
117 }
118
119 pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> {
127 self.maps.iter().flat_map(|(_, map)| map.inner_nodes())
128 }
129
130 pub fn maps(&self) -> impl Iterator<Item = &PartialStorageMap> + '_ {
132 self.maps.values()
133 }
134
135 pub fn leaves(&self) -> impl Iterator<Item = &SmtLeaf> + '_ {
137 self.maps().flat_map(|map| map.leaves()).map(|(_, leaf)| leaf)
138 }
139}
140
141impl Serializable for PartialStorage {
142 fn write_into<W: ByteWriter>(&self, target: &mut W) {
143 target.write(&self.header);
144 target.write(&self.maps);
145 }
146}
147
148impl Deserializable for PartialStorage {
149 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
150 let header: AccountStorageHeader = source.read()?;
151 let map_smts: BTreeMap<Word, PartialStorageMap> = source.read()?;
152
153 let commitment = header.to_commitment();
154
155 Ok(PartialStorage { header, maps: map_smts, commitment })
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use anyhow::Context;
162 use miden_core::Word;
163
164 use crate::account::{
165 AccountStorage,
166 AccountStorageHeader,
167 PartialStorage,
168 PartialStorageMap,
169 StorageMap,
170 StorageMapKey,
171 StorageSlot,
172 StorageSlotName,
173 };
174
175 #[test]
176 pub fn new_partial_storage() -> anyhow::Result<()> {
177 let map_key_present = StorageMapKey::from_array([1, 2, 3, 4]);
178 let map_key_absent = StorageMapKey::from_array([9, 12, 18, 3]);
179
180 let mut map_1 = StorageMap::new();
181 map_1.insert(map_key_absent, Word::try_from([1u64, 2, 3, 2])?).unwrap();
182 map_1.insert(map_key_present, Word::try_from([5u64, 4, 3, 2])?).unwrap();
183 assert_eq!(map_1.get(&map_key_present), [5u64, 4, 3, 2].try_into()?);
184
185 let slot_name = StorageSlotName::new("miden::test_map")?;
186
187 let storage =
188 AccountStorage::new(vec![StorageSlot::with_map(slot_name.clone(), map_1.clone())])
189 .unwrap();
190
191 let storage_header = AccountStorageHeader::from(&storage);
193 let witness = map_1.open(&map_key_present);
194
195 let partial_storage =
196 PartialStorage::new(storage_header, [PartialStorageMap::with_witnesses([witness])?])
197 .context("creating partial storage")?;
198
199 let slot_header = partial_storage.header.find_slot_header_by_name(&slot_name).unwrap();
200 let retrieved_map = partial_storage.maps.get(&slot_header.value()).unwrap();
201 assert!(retrieved_map.open(&map_key_absent).is_err());
202 assert!(retrieved_map.open(&map_key_present).is_ok());
203 Ok(())
204 }
205}