miden_protocol/account/storage/
partial.rs1use alloc::collections::{BTreeMap, BTreeSet};
2
3use miden_core::utils::{Deserializable, Serializable};
4use miden_crypto::Word;
5use miden_crypto::merkle::InnerNodeInfo;
6use miden_crypto::merkle::smt::SmtLeaf;
7
8use super::{AccountStorage, AccountStorageHeader, StorageSlotContent};
9use crate::account::PartialStorageMap;
10use crate::errors::AccountError;
11
12#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct PartialStorage {
19 commitment: Word,
21 header: AccountStorageHeader,
23 maps: BTreeMap<Word, PartialStorageMap>,
26}
27
28impl PartialStorage {
29 pub fn new(
38 storage_header: AccountStorageHeader,
39 storage_maps: impl IntoIterator<Item = PartialStorageMap>,
40 ) -> Result<Self, AccountError> {
41 let storage_map_roots: BTreeSet<_> = storage_header.map_slot_roots().collect();
42 let mut maps = BTreeMap::new();
43 for smt in storage_maps {
44 if !storage_map_roots.contains(&smt.root()) {
46 return Err(AccountError::StorageMapRootNotFound(smt.root()));
47 }
48 maps.insert(smt.root(), smt);
49 }
50
51 let commitment = storage_header.to_commitment();
52 Ok(Self { commitment, header: storage_header, maps })
53 }
54
55 pub fn new_full(account_storage: AccountStorage) -> Self {
60 let header: AccountStorageHeader = account_storage.to_header();
61 let commitment = header.to_commitment();
62
63 let mut maps = BTreeMap::new();
64 for slot in account_storage {
65 if let StorageSlotContent::Map(storage_map) = slot.into_parts().1 {
66 let partial_map = PartialStorageMap::new_full(storage_map);
67 maps.insert(partial_map.root(), partial_map);
68 }
69 }
70
71 PartialStorage { header, maps, commitment }
72 }
73
74 pub fn new_minimal(account_storage: &AccountStorage) -> Self {
79 let header: AccountStorageHeader = account_storage.to_header();
80 let commitment = header.to_commitment();
81
82 let mut maps = BTreeMap::new();
83 for slot in account_storage.slots() {
84 if let StorageSlotContent::Map(storage_map) = slot.content() {
85 let partial_map = PartialStorageMap::new_minimal(storage_map);
86 maps.insert(partial_map.root(), partial_map);
87 }
88 }
89
90 PartialStorage { header, maps, commitment }
91 }
92
93 pub fn header(&self) -> &AccountStorageHeader {
98 &self.header
99 }
100
101 pub fn commitment(&self) -> Word {
103 self.commitment
104 }
105
106 pub fn into_parts(self) -> (Word, AccountStorageHeader, BTreeMap<Word, PartialStorageMap>) {
110 (self.commitment, self.header, self.maps)
111 }
112
113 pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> {
121 self.maps.iter().flat_map(|(_, map)| map.inner_nodes())
122 }
123
124 pub fn maps(&self) -> impl Iterator<Item = &PartialStorageMap> + '_ {
126 self.maps.values()
127 }
128
129 pub fn leaves(&self) -> impl Iterator<Item = &SmtLeaf> + '_ {
131 self.maps().flat_map(|map| map.leaves()).map(|(_, leaf)| leaf)
132 }
133}
134
135impl Serializable for PartialStorage {
136 fn write_into<W: miden_core::utils::ByteWriter>(&self, target: &mut W) {
137 target.write(&self.header);
138 target.write(&self.maps);
139 }
140}
141
142impl Deserializable for PartialStorage {
143 fn read_from<R: miden_core::utils::ByteReader>(
144 source: &mut R,
145 ) -> Result<Self, miden_processor::DeserializationError> {
146 let header: AccountStorageHeader = source.read()?;
147 let map_smts: BTreeMap<Word, PartialStorageMap> = source.read()?;
148
149 let commitment = header.to_commitment();
150
151 Ok(PartialStorage { header, maps: map_smts, commitment })
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use anyhow::Context;
158 use miden_core::Word;
159
160 use crate::account::{
161 AccountStorage,
162 AccountStorageHeader,
163 PartialStorage,
164 PartialStorageMap,
165 StorageMap,
166 StorageSlot,
167 StorageSlotName,
168 };
169
170 #[test]
171 pub fn new_partial_storage() -> anyhow::Result<()> {
172 let map_key_present: Word = [1u64, 2, 3, 4].try_into()?;
173 let map_key_absent: Word = [9u64, 12, 18, 3].try_into()?;
174
175 let mut map_1 = StorageMap::new();
176 map_1.insert(map_key_absent, Word::try_from([1u64, 2, 3, 2])?).unwrap();
177 map_1.insert(map_key_present, Word::try_from([5u64, 4, 3, 2])?).unwrap();
178 assert_eq!(map_1.get(&map_key_present), [5u64, 4, 3, 2].try_into()?);
179
180 let slot_name = StorageSlotName::new("miden::test_map")?;
181
182 let storage =
183 AccountStorage::new(vec![StorageSlot::with_map(slot_name.clone(), map_1.clone())])
184 .unwrap();
185
186 let storage_header = AccountStorageHeader::from(&storage);
188 let witness = map_1.open(&map_key_present);
189
190 let partial_storage =
191 PartialStorage::new(storage_header, [PartialStorageMap::with_witnesses([witness])?])
192 .context("creating partial storage")?;
193
194 let slot_header = partial_storage.header.find_slot_header_by_name(&slot_name).unwrap();
195 let retrieved_map = partial_storage.maps.get(&slot_header.value()).unwrap();
196 assert!(retrieved_map.open(&map_key_absent).is_err());
197 assert!(retrieved_map.open(&map_key_present).is_ok());
198 Ok(())
199 }
200}