miden_node_store/state/view/account/
mod.rs1use std::collections::HashSet;
2
3use miden_node_proto::domain::account::{
4 AccountDetailRequest,
5 AccountDetails,
6 AccountStorageDetails,
7 AccountStorageMapDetails,
8 AccountStorageRequest,
9 AccountVaultDetails,
10 GetAccountRequest,
11 GetAccountResponse,
12 SlotData,
13 StorageMapEntries,
14 StorageMapRequest,
15};
16use miden_node_tracing::miden_instrument;
17use miden_protocol::account::{AccountId, AccountStorageHeader, StorageSlotName, StorageSlotType};
18use miden_protocol::block::BlockNumber;
19use miden_protocol::block::account_tree::AccountWitness;
20
21use super::{ScopedBlockNum, StateView};
22use crate::COMPONENT;
23use crate::account_state_forest::AccountStorageMapResult;
24use crate::errors::{DatabaseError, GetAccountError};
25
26impl StateView {
27 #[miden_instrument(
37 target = COMPONENT,
38 )]
39 pub async fn get_account(
40 &self,
41 get_account_request: GetAccountRequest,
42 ) -> Result<GetAccountResponse, GetAccountError> {
43 let GetAccountRequest { block_num, account_id, details } = get_account_request;
44
45 if details.is_some() && !account_id.is_public() {
46 return Err(GetAccountError::AccountNotPublic(account_id));
47 }
48
49 let (scoped_block, witness) = self.get_account_witness(block_num, account_id).await?;
50
51 let details = if let Some(request) = details {
52 Some(
53 self.fetch_public_account_details(account_id, scoped_block, &witness, request)
54 .await?,
55 )
56 } else {
57 None
58 };
59
60 Ok(GetAccountResponse {
61 block_num: *scoped_block,
62 witness,
63 details,
64 })
65 }
66
67 #[miden_instrument(
74 target = COMPONENT,
75 )]
76 async fn get_account_witness(
77 &self,
78 block_num: Option<BlockNumber>,
79 account_id: AccountId,
80 ) -> Result<(ScopedBlockNum, AccountWitness), GetAccountError> {
81 if let Some(requested_block) = block_num {
84 let scoped_block = self
85 .scope_block(requested_block)
86 .ok_or(GetAccountError::UnknownBlock(requested_block))?;
87 let witness = self
88 .with_inner_read_blocking(|inner_state| {
89 inner_state.account_tree.open_at(account_id, *scoped_block)
90 })
91 .ok_or(GetAccountError::BlockPruned(*scoped_block))?;
92 Ok((scoped_block, witness))
93 } else {
94 let witness = self.with_inner_read_blocking(|inner_state| {
96 inner_state.account_tree.open_latest(account_id)
97 });
98 Ok((self.tip(), witness))
99 }
100 }
101
102 #[miden_instrument(
109 target = COMPONENT,
110 )]
111 fn get_storage_map_details_from_forest(
112 &self,
113 account_id: AccountId,
114 slot_name: &StorageSlotName,
115 block_num: ScopedBlockNum,
116 ) -> Result<Option<AccountStorageMapDetails>, DatabaseError> {
117 self.with_forest_read_blocking(|forest| {
118 match forest
119 .get_storage_map_details_for_all_entries(account_id, slot_name.clone(), *block_num)
120 .map_err(DatabaseError::MerkleError)?
121 {
122 AccountStorageMapResult::NotFound => Err(DatabaseError::StorageRootNotFound {
123 account_id,
124 slot_name: slot_name.to_string(),
125 block_num: *block_num,
126 }),
127 AccountStorageMapResult::Details(details) => Ok(Some(details)),
128 AccountStorageMapResult::CannotReconstructKeysFromCache => Ok(None),
129 }
130 })
131 }
132
133 async fn reconstruct_vault_details_from_db(
135 &self,
136 account_id: AccountId,
137 block_num: ScopedBlockNum,
138 ) -> Result<AccountVaultDetails, DatabaseError> {
139 let assets = self.db.select_vault_at_block(account_id, block_num).await?;
140
141 if assets.len() > AccountVaultDetails::MAX_RETURN_ENTRIES {
142 return Ok(AccountVaultDetails::LimitExceeded);
143 }
144
145 let keys = assets.iter().map(miden_protocol::asset::Asset::id);
146
147 self.with_forest_read_blocking(|forest| {
150 forest
151 .vault_key_cache
152 .put_many(keys.into_iter().map(|raw_key| (raw_key.hash(), raw_key)));
153 });
154
155 Ok(AccountVaultDetails::from_assets(assets))
156 }
157
158 async fn reconstruct_storage_map_details_from_db(
160 &self,
161 account_id: AccountId,
162 slot_name: StorageSlotName,
163 block_num: ScopedBlockNum,
164 ) -> Result<AccountStorageMapDetails, DatabaseError> {
165 let details = self
166 .db
167 .reconstruct_storage_map_from_db(
168 account_id,
169 slot_name,
170 block_num,
171 Some(AccountStorageMapDetails::MAX_RETURN_ENTRIES),
172 )
173 .await?;
174
175 if let StorageMapEntries::AllEntries(entries) = &details.entries {
176 self.with_forest_read_blocking(|forest| {
177 forest.cache_storage_map_keys(entries.iter().map(|(raw_key, _)| *raw_key));
178 });
179 }
180
181 Ok(details)
182 }
183
184 #[miden_instrument(
195 target = COMPONENT,
196 )]
197 async fn fetch_public_account_details(
198 &self,
199 account_id: AccountId,
200 scoped_block: ScopedBlockNum,
201 witness: &AccountWitness,
202 detail_request: AccountDetailRequest,
203 ) -> Result<AccountDetails, GetAccountError> {
204 let AccountDetailRequest {
205 code_commitment,
206 asset_vault_commitment,
207 storage_request,
208 } = detail_request;
209
210 if !account_id.is_public() {
211 return Err(GetAccountError::AccountNotPublic(account_id));
212 }
213
214 let (account_header, storage_header) = self
216 .db
217 .select_account_header_with_storage_header_at_block(account_id, scoped_block)
218 .await?
219 .ok_or(GetAccountError::AccountNotFound(account_id, *scoped_block))?;
220
221 let should_apply_response_budget =
222 matches!(&storage_request, AccountStorageRequest::AllStorageMaps);
223 let storage_requests = expand_account_storage_request(storage_request, &storage_header);
224
225 let account_code = match code_commitment {
226 Some(commitment) if commitment == account_header.code_commitment() => None,
227 Some(_) => {
228 self.db
229 .select_account_code_by_commitment(account_header.code_commitment())
230 .await?
231 },
232 None => None,
233 };
234
235 let vault_details = match asset_vault_commitment {
241 Some(commitment) if commitment == account_header.vault_root() => {
242 AccountVaultDetails::empty()
243 },
244 Some(_) => {
245 let forest_details = self.with_forest_read_blocking(|forest| {
246 forest.get_vault_details(account_id, *scoped_block).map_err(|err| {
247 DatabaseError::DataCorrupted(format!(
248 "failed to reconstruct vault for account {account_id} at block {}: {err}",
249 *scoped_block,
250 ))
251 })
252 })?;
253
254 match forest_details {
255 Some(details) => details,
256 None => {
257 self.reconstruct_vault_details_from_db(account_id, scoped_block).await?
258 },
259 }
260 },
261 None => AccountVaultDetails::empty(),
262 };
263
264 let mut storage_map_details =
268 Vec::<AccountStorageMapDetails>::with_capacity(storage_requests.len());
269 let mut map_keys_requests = Vec::new();
270 let mut all_entries_requests = Vec::new();
271 let mut storage_request_slots = Vec::with_capacity(storage_requests.len());
272
273 for (index, StorageMapRequest { slot_name, slot_data }) in
274 storage_requests.into_iter().enumerate()
275 {
276 storage_request_slots.push(slot_name.clone());
277 match slot_data {
278 SlotData::MapKeys(keys) => {
279 map_keys_requests.push((index, slot_name, keys));
280 },
281 SlotData::All => {
282 all_entries_requests.push((index, slot_name));
283 },
284 }
285 }
286
287 let mut storage_map_details_by_index = vec![None; storage_request_slots.len()];
288
289 if !map_keys_requests.is_empty() {
291 self.with_forest_read_blocking(|forest| {
292 for (index, slot_name, keys) in map_keys_requests {
293 let details = forest
294 .get_storage_map_details_for_keys(
295 account_id,
296 slot_name.clone(),
297 *scoped_block,
298 keys,
299 )
300 .ok_or_else(|| DatabaseError::StorageRootNotFound {
301 account_id,
302 slot_name: slot_name.to_string(),
303 block_num: *scoped_block,
304 })?
305 .map_err(DatabaseError::MerkleError)?;
306 storage_map_details_by_index[index] = Some(details);
307 }
308 Ok::<(), DatabaseError>(())
309 })?;
310 }
311
312 for (index, slot_name) in all_entries_requests {
314 let details = match self.get_storage_map_details_from_forest(
315 account_id,
316 &slot_name,
317 scoped_block,
318 )? {
319 Some(details) => details,
320 None => {
321 self.reconstruct_storage_map_details_from_db(
322 account_id,
323 slot_name,
324 scoped_block,
325 )
326 .await?
327 },
328 };
329 storage_map_details_by_index[index] = Some(details);
330 }
331
332 for (details, slot_name) in
333 storage_map_details_by_index.into_iter().zip(storage_request_slots.iter())
334 {
335 let details = details.ok_or_else(|| DatabaseError::StorageRootNotFound {
336 account_id,
337 slot_name: slot_name.to_string(),
338 block_num: *scoped_block,
339 })?;
340 storage_map_details.push(details);
341 }
342
343 if should_apply_response_budget {
348 return Ok(apply_all_storage_maps_response_budget(
349 *scoped_block,
350 witness,
351 account_header,
352 account_code,
353 vault_details,
354 storage_header,
355 storage_map_details,
356 storage_request_slots,
357 MAX_ALL_STORAGE_MAPS_RESPONSE_PAYLOAD_WITH_BUDGET_RESERVED_FOR_LIMIT_EXCEEDED_SLOTS,
358 ));
359 }
360
361 Ok(AccountDetails {
362 account_header,
363 account_code,
364 vault_details,
365 storage_details: AccountStorageDetails {
366 header: storage_header,
367 map_details: storage_map_details,
368 },
369 })
370 }
371}
372
373fn expand_account_storage_request(
378 storage_request: AccountStorageRequest,
379 storage_header: &AccountStorageHeader,
380) -> Vec<StorageMapRequest> {
381 match storage_request {
382 AccountStorageRequest::None => Vec::new(),
383 AccountStorageRequest::Explicit(requests) => requests,
384 AccountStorageRequest::AllStorageMaps => storage_header
385 .slots()
386 .filter(|slot| slot.slot_type() == StorageSlotType::Map)
387 .map(|slot| StorageMapRequest {
388 slot_name: slot.name().clone(),
389 slot_data: SlotData::All,
390 })
391 .collect(),
392 }
393}
394
395mod response_budget;
396use response_budget::{
397 MAX_ALL_STORAGE_MAPS_RESPONSE_PAYLOAD_WITH_BUDGET_RESERVED_FOR_LIMIT_EXCEEDED_SLOTS,
398 apply_all_storage_maps_response_budget,
399};
400
401impl StateView {
405 pub async fn filter_network_accounts(
407 &self,
408 account_ids: &[AccountId],
409 ) -> Result<HashSet<AccountId>, DatabaseError> {
410 self.db.filter_network_accounts(account_ids.to_vec()).await
411 }
412}