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