1use alloc::collections::BTreeMap;
2use alloc::vec::Vec;
3use core::fmt::{self, Debug, Display, Formatter};
4
5use miden_protocol::account::{
6 Account, AccountCode, AccountHeader, AccountId, AccountStorage, AccountStorageHeader,
7 StorageMap, StorageMapKey, StorageSlot, StorageSlotHeader, StorageSlotName, StorageSlotType,
8};
9use miden_protocol::asset::{Asset, AssetVault};
10use miden_protocol::block::BlockNumber;
11use miden_protocol::block::account_tree::AccountWitness;
12use miden_protocol::crypto::merkle::SparseMerklePath;
13use miden_protocol::crypto::merkle::smt::PartialSmt;
14use miden_protocol::{EMPTY_WORD, Word};
15use miden_tx::utils::ToHex;
16use miden_tx::utils::serde::{Deserializable, Serializable};
17use thiserror::Error;
18
19use crate::alloc::string::ToString;
20use crate::rpc::{AccountStateAt, RpcError};
21use crate::rpc::domain::MissingFieldHelper;
22use crate::rpc::errors::RpcConversionError;
23use crate::rpc::generated::rpc::account_request::account_detail_request::storage_map_detail_request::{MapKeys, SlotData};
24use crate::rpc::generated::rpc::account_request::account_detail_request::{
25 StorageMapDetailRequest, StorageMapDetailRequests, StorageRequest,
26};
27use crate::rpc::generated::{self as proto};
28
29impl Display for proto::account::AccountId {
33 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
34 f.write_fmt(format_args!("0x{}", self.id.to_hex()))
35 }
36}
37
38impl Debug for proto::account::AccountId {
39 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
40 Display::fmt(self, f)
41 }
42}
43
44impl From<AccountId> for proto::account::AccountId {
48 fn from(account_id: AccountId) -> Self {
49 Self { id: account_id.to_bytes() }
50 }
51}
52
53impl TryFrom<proto::account::AccountId> for AccountId {
57 type Error = RpcConversionError;
58
59 fn try_from(account_id: proto::account::AccountId) -> Result<Self, Self::Error> {
60 AccountId::read_from_bytes(&account_id.id).map_err(|_| RpcConversionError::NotAValidFelt)
61 }
62}
63
64impl TryInto<AccountHeader> for proto::account::AccountHeader {
68 type Error = crate::rpc::RpcError;
69
70 fn try_into(self) -> Result<AccountHeader, Self::Error> {
71 use miden_protocol::Felt;
72
73 use crate::rpc::domain::MissingFieldHelper;
74
75 let proto::account::AccountHeader {
76 account_id,
77 nonce,
78 vault_root,
79 storage_commitment,
80 code_commitment,
81 } = self;
82
83 let account_id: AccountId = account_id
84 .ok_or(proto::account::AccountHeader::missing_field(stringify!(account_id)))?
85 .try_into()?;
86 let vault_root = vault_root
87 .ok_or(proto::account::AccountHeader::missing_field(stringify!(vault_root)))?
88 .try_into()?;
89 let storage_commitment = storage_commitment
90 .ok_or(proto::account::AccountHeader::missing_field(stringify!(storage_commitment)))?
91 .try_into()?;
92 let code_commitment = code_commitment
93 .ok_or(proto::account::AccountHeader::missing_field(stringify!(code_commitment)))?
94 .try_into()?;
95
96 let nonce = Felt::new(nonce).map_err(|_| RpcConversionError::NotAValidFelt)?;
97 Ok(AccountHeader::new(
98 account_id,
99 nonce,
100 vault_root,
101 storage_commitment,
102 code_commitment,
103 ))
104 }
105}
106
107impl TryInto<AccountStorageHeader> for proto::account::AccountStorageHeader {
111 type Error = crate::rpc::RpcError;
112
113 fn try_into(self) -> Result<AccountStorageHeader, Self::Error> {
114 use crate::rpc::RpcError;
115 use crate::rpc::domain::MissingFieldHelper;
116
117 let mut header_slots: Vec<StorageSlotHeader> = Vec::with_capacity(self.slots.len());
118
119 for slot in self.slots {
120 let slot_value: Word = slot
121 .commitment
122 .ok_or(proto::account::account_storage_header::StorageSlot::missing_field(
123 stringify!(commitment),
124 ))?
125 .try_into()?;
126
127 let slot_type = u8::try_from(slot.slot_type)
128 .map_err(|e| RpcError::InvalidResponse(e.to_string()))
129 .and_then(|v| {
130 StorageSlotType::try_from(v)
131 .map_err(|e| RpcError::InvalidResponse(e.to_string()))
132 })?;
133 let slot_name = StorageSlotName::new(slot.slot_name)
134 .map_err(|err| RpcError::InvalidResponse(err.to_string()))?;
135
136 header_slots.push(StorageSlotHeader::new(slot_name, slot_type, slot_value));
137 }
138
139 header_slots.sort_by_key(StorageSlotHeader::id);
140 AccountStorageHeader::new(header_slots)
141 .map_err(|err| RpcError::InvalidResponse(err.to_string()))
142 }
143}
144
145#[cfg(feature = "tonic")]
149impl proto::rpc::account_response::AccountDetails {
150 pub fn into_domain(
164 self,
165 known_account_codes: &BTreeMap<Word, AccountCode>,
166 storage_requirements: &AccountStorageRequirements,
167 ) -> Result<AccountDetails, crate::rpc::RpcError> {
168 use crate::rpc::RpcError;
169 use crate::rpc::domain::MissingFieldHelper;
170
171 let proto::rpc::account_response::AccountDetails {
172 header,
173 storage_details,
174 code,
175 vault_details,
176 } = self;
177 let header: AccountHeader = header
178 .ok_or(proto::rpc::account_response::AccountDetails::missing_field(stringify!(header)))?
179 .try_into()?;
180
181 let storage_details: AccountStorageDetails = storage_details
182 .ok_or(proto::rpc::account_response::AccountDetails::missing_field(stringify!(
183 storage_details
184 )))?
185 .try_into()?;
186
187 storage_details.validate_against_request(storage_requirements)?;
188
189 let code = {
193 let received_code = code.map(|c| AccountCode::read_from_bytes(&c)).transpose()?;
194 match received_code {
195 Some(code) => code,
196 None => known_account_codes
197 .get(&header.code_commitment())
198 .ok_or(RpcError::InvalidResponse(
199 "Account code was not provided, but the response did not contain it either"
200 .into(),
201 ))?
202 .clone(),
203 }
204 };
205
206 let vault_details = vault_details
207 .ok_or(proto::rpc::AccountVaultDetails::missing_field(stringify!(vault_details)))?
208 .try_into()?;
209
210 Ok(AccountDetails {
211 header,
212 storage_details,
213 code,
214 vault_details,
215 })
216 }
217}
218
219pub type AccountProofs = (BlockNumber, Vec<AccountProof>);
224
225#[derive(Clone, Debug)]
230pub struct AccountDetails {
231 pub header: AccountHeader,
232 pub storage_details: AccountStorageDetails,
233 pub code: AccountCode,
234 pub vault_details: AccountVaultDetails,
235}
236
237impl TryFrom<&AccountDetails> for Account {
238 type Error = RpcError;
239
240 fn try_from(details: &AccountDetails) -> Result<Self, Self::Error> {
246 if details.vault_details.too_many_assets {
247 return Err(RpcError::ExpectedDataMissing(
248 "cannot build account: vault has too many assets".into(),
249 ));
250 }
251
252 if let Some(slot_name) = details
253 .storage_details
254 .map_details
255 .iter()
256 .find(|m| m.is_limit_exceeded())
257 .map(|m| &m.slot_name)
258 {
259 return Err(RpcError::ExpectedDataMissing(format!(
260 "cannot build account: storage map slot '{slot_name}' has too many entries",
261 )));
262 }
263
264 let mut slots: Vec<StorageSlot> = Vec::new();
265
266 for slot_header in details.storage_details.header.slots() {
267 match slot_header.slot_type() {
268 StorageSlotType::Value => {
269 slots.push(StorageSlot::with_value(
270 slot_header.name().clone(),
271 slot_header.value(),
272 ));
273 },
274 StorageSlotType::Map => {
275 let map_details = details
276 .storage_details
277 .find_map_details(slot_header.name())
278 .ok_or_else(|| {
279 RpcError::ExpectedDataMissing(format!(
280 "slot '{}' is a map but has no map_details in response",
281 slot_header.name()
282 ))
283 })?;
284
285 let storage_map = map_details
286 .entries
287 .clone()
288 .into_storage_map()
289 .ok_or_else(|| {
290 RpcError::ExpectedDataMissing(format!(
291 "slot '{}' did not come back with all its entries, so the full \
292 account cannot be built",
293 slot_header.name(),
294 ))
295 })?
296 .map_err(|err| {
297 RpcError::InvalidResponse(format!(
298 "the rpc api returned a non-valid map entry: {err}"
299 ))
300 })?;
301
302 slots.push(StorageSlot::with_map(slot_header.name().clone(), storage_map));
303 },
304 }
305 }
306
307 let asset_vault = AssetVault::new(&details.vault_details.assets).map_err(|err| {
308 RpcError::InvalidResponse(format!("rpc api returned non-valid assets: {err}"))
309 })?;
310
311 let account_storage = AccountStorage::new(slots).map_err(|err| {
312 RpcError::InvalidResponse(format!("rpc api returned non-valid storage slots: {err}"))
313 })?;
314
315 Account::new(
316 details.header.id(),
317 asset_vault,
318 account_storage,
319 details.code.clone(),
320 details.header.nonce(),
321 None,
322 )
323 .map_err(|err| {
324 RpcError::InvalidResponse(format!(
325 "failed to construct account from rpc api response: {err}"
326 ))
327 })
328 }
329}
330
331#[derive(Clone, Debug)]
336pub struct AccountStorageDetails {
337 pub header: AccountStorageHeader,
339 pub map_details: Vec<AccountStorageMapDetails>,
341}
342
343impl AccountStorageDetails {
344 pub fn find_map_details(&self, target: &StorageSlotName) -> Option<&AccountStorageMapDetails> {
348 self.map_details.iter().find(|map_detail| map_detail.slot_name == *target)
349 }
350
351 pub fn validate_against_request(
357 &self,
358 storage_requirements: &AccountStorageRequirements,
359 ) -> Result<(), RpcError> {
360 for map_detail in &self.map_details {
361 let StorageMapEntries::PartialMap { map_keys, .. } = &map_detail.entries else {
362 continue;
363 };
364
365 let requested_keys = storage_requirements.keys_for_slot(&map_detail.slot_name);
366 if map_keys.len() != requested_keys.len() {
367 return Err(RpcError::InvalidResponse(format!(
368 "expected {} keys for storage map slot '{}', got {}",
369 requested_keys.len(),
370 map_detail.slot_name,
371 map_keys.len(),
372 )));
373 }
374 if let Some(key) = map_keys.iter().find(|key| !requested_keys.contains(key)) {
375 return Err(RpcError::InvalidResponse(format!(
376 "partial storage map for slot '{}' covers key {}, which was not requested",
377 map_detail.slot_name,
378 key.to_hex(),
379 )));
380 }
381 }
382
383 Ok(())
384 }
385}
386
387impl TryFrom<proto::rpc::AccountStorageDetails> for AccountStorageDetails {
388 type Error = RpcError;
389
390 fn try_from(value: proto::rpc::AccountStorageDetails) -> Result<Self, Self::Error> {
391 let header: AccountStorageHeader = value
392 .header
393 .ok_or(proto::account::AccountStorageHeader::missing_field(stringify!(header)))?
394 .try_into()?;
395 let map_details = value
396 .map_details
397 .into_iter()
398 .map(core::convert::TryInto::try_into)
399 .collect::<Result<Vec<AccountStorageMapDetails>, RpcError>>()?;
400
401 for map_detail in &map_details {
405 let StorageMapEntries::PartialMap { partial_smt, .. } = &map_detail.entries else {
406 continue;
407 };
408
409 let slot = header
410 .slots()
411 .find(|slot| *slot.name() == map_detail.slot_name)
412 .ok_or_else(|| {
413 RpcError::InvalidResponse(format!(
414 "partial storage map references slot '{}', which is absent from the \
415 storage header",
416 map_detail.slot_name,
417 ))
418 })?;
419 if slot.slot_type() != StorageSlotType::Map {
420 return Err(RpcError::InvalidResponse(format!(
421 "partial storage map references slot '{}', which is not a map",
422 map_detail.slot_name,
423 )));
424 }
425 if partial_smt.root() != slot.value() {
426 return Err(RpcError::InvalidResponse(format!(
427 "partial storage map for slot '{}' has root {} but the storage header reports \
428 {}",
429 map_detail.slot_name,
430 partial_smt.root(),
431 slot.value(),
432 )));
433 }
434 }
435
436 Ok(Self { header, map_details })
437 }
438}
439
440#[derive(Clone, Debug)]
444pub struct AccountStorageMapDetails {
445 pub slot_name: StorageSlotName,
447 pub entries: StorageMapEntries,
449}
450
451impl AccountStorageMapDetails {
452 pub const MAX_PARTIAL_MAP_KEYS: usize = 64;
455
456 pub fn is_limit_exceeded(&self) -> bool {
460 matches!(self.entries, StorageMapEntries::LimitExceeded)
461 }
462}
463
464impl TryFrom<proto::rpc::account_storage_details::AccountStorageMapDetails>
465 for AccountStorageMapDetails
466{
467 type Error = RpcError;
468
469 fn try_from(
470 value: proto::rpc::account_storage_details::AccountStorageMapDetails,
471 ) -> Result<Self, Self::Error> {
472 use proto::rpc::account_storage_details::account_storage_map_details::Result as ProtoResult;
473
474 let slot_name = StorageSlotName::new(value.slot_name)
475 .map_err(|err| RpcError::ExpectedDataMissing(err.to_string()))?;
476
477 let entries = match value.result {
478 Some(ProtoResult::TooManyEntries(true)) => StorageMapEntries::LimitExceeded,
479 Some(ProtoResult::TooManyEntries(false)) => {
480 return Err(RpcError::InvalidResponse(
481 "too_many_entries must be true when set".into(),
482 ));
483 },
484 Some(ProtoResult::AllEntries(all_entries)) => {
485 let entries = all_entries
486 .entries
487 .into_iter()
488 .map(core::convert::TryInto::try_into)
489 .collect::<Result<Vec<StorageMapEntry>, RpcError>>()?;
490 StorageMapEntries::AllEntries(entries)
491 },
492 Some(ProtoResult::PartialMap(partial_map)) => {
493 if partial_map.map_keys.len() > Self::MAX_PARTIAL_MAP_KEYS {
494 return Err(RpcError::InvalidResponse(format!(
495 "partial storage map for slot '{slot_name}' contains {} keys, exceeding \
496 the limit of {}",
497 partial_map.map_keys.len(),
498 Self::MAX_PARTIAL_MAP_KEYS,
499 )));
500 }
501
502 let map_keys = partial_map
503 .map_keys
504 .into_iter()
505 .map(|key| Word::try_from(key).map(StorageMapKey::new))
506 .collect::<Result<Vec<_>, _>>()?;
507 if let Some(key) = first_duplicate_key(&map_keys) {
508 return Err(RpcError::InvalidResponse(format!(
509 "partial storage map for slot '{slot_name}' repeats key {}",
510 key.to_hex(),
511 )));
512 }
513
514 let partial_smt: PartialSmt = partial_map
515 .partial_smt
516 .ok_or(proto::rpc::account_storage_details::account_storage_map_details::PartialStorageMap::missing_field(
517 stringify!(partial_smt),
518 ))?
519 .try_into()?;
520
521 for key in &map_keys {
524 partial_smt.get_value(&key.hash().as_word()).map_err(|_| {
525 RpcError::InvalidResponse(format!(
526 "partial storage map for slot '{slot_name}' does not track key {}",
527 key.to_hex(),
528 ))
529 })?;
530 }
531
532 StorageMapEntries::PartialMap { map_keys, partial_smt }
533 },
534 None => {
535 return Err(RpcError::InvalidResponse(format!(
536 "storage map details for slot '{slot_name}' carry no result",
537 )));
538 },
539 };
540
541 Ok(Self { slot_name, entries })
542 }
543}
544
545fn first_duplicate_key(keys: &[StorageMapKey]) -> Option<&StorageMapKey> {
550 keys.iter()
551 .enumerate()
552 .find_map(|(index, key)| keys[..index].contains(key).then_some(key))
553}
554
555#[derive(Clone, Debug)]
560pub struct StorageMapEntry {
561 pub key: StorageMapKey,
562 pub value: Word,
563}
564
565impl TryFrom<proto::rpc::account_storage_details::account_storage_map_details::all_map_entries::StorageMapEntry>
566 for StorageMapEntry
567{
568 type Error = RpcError;
569
570 fn try_from(value: proto::rpc::account_storage_details::account_storage_map_details::all_map_entries::StorageMapEntry) -> Result<Self, Self::Error> {
571 let key: StorageMapKey =
572 value.key.ok_or(RpcError::ExpectedDataMissing("key".into()))?.try_into()?;
573 let value = value.value.ok_or(RpcError::ExpectedDataMissing("value".into()))?.try_into()?;
574 Ok(Self { key, value })
575 }
576}
577
578#[derive(Clone, Debug)]
584pub enum StorageMapEntries {
585 LimitExceeded,
588 AllEntries(Vec<StorageMapEntry>),
590 PartialMap {
596 map_keys: Vec<StorageMapKey>,
598 partial_smt: PartialSmt,
600 },
601}
602
603impl StorageMapEntries {
604 pub fn into_storage_map(
609 self,
610 ) -> Option<Result<StorageMap, miden_protocol::errors::StorageMapError>> {
611 match self {
612 StorageMapEntries::AllEntries(entries) => {
613 Some(StorageMap::with_entries(entries.into_iter().map(|e| (e.key, e.value))))
614 },
615 StorageMapEntries::LimitExceeded | StorageMapEntries::PartialMap { .. } => None,
616 }
617 }
618}
619
620#[derive(Clone, Debug)]
624pub struct AccountVaultDetails {
625 pub too_many_assets: bool,
629 pub assets: Vec<Asset>,
632}
633
634impl TryFrom<proto::rpc::AccountVaultDetails> for AccountVaultDetails {
635 type Error = RpcError;
636
637 fn try_from(value: proto::rpc::AccountVaultDetails) -> Result<Self, Self::Error> {
638 let too_many_assets = value.too_many_assets;
639 let assets = value
640 .assets
641 .into_iter()
642 .map(Asset::try_from)
643 .collect::<Result<Vec<Asset>, _>>()?;
644
645 Ok(Self { too_many_assets, assets })
646 }
647}
648
649#[derive(Clone, Debug)]
654pub struct AccountProof {
655 account_witness: AccountWitness,
657 state_headers: Option<AccountDetails>,
659}
660
661impl AccountProof {
662 pub fn new(
664 account_witness: AccountWitness,
665 account_details: Option<AccountDetails>,
666 ) -> Result<Self, AccountProofError> {
667 if let Some(AccountDetails {
668 header: account_header,
669 storage_details: _,
670 code,
671 ..
672 }) = &account_details
673 {
674 if account_header.to_commitment() != account_witness.state_commitment() {
675 return Err(AccountProofError::InconsistentAccountCommitment);
676 }
677 if account_header.id() != account_witness.id() {
678 return Err(AccountProofError::InconsistentAccountId);
679 }
680 if code.commitment() != account_header.code_commitment() {
681 return Err(AccountProofError::InconsistentCodeCommitment);
682 }
683 }
684
685 Ok(Self {
686 account_witness,
687 state_headers: account_details,
688 })
689 }
690
691 pub fn account_id(&self) -> AccountId {
693 self.account_witness.id()
694 }
695
696 pub fn account_header(&self) -> Option<&AccountHeader> {
698 self.state_headers.as_ref().map(|account_details| &account_details.header)
699 }
700
701 pub fn storage_header(&self) -> Option<&AccountStorageHeader> {
703 self.state_headers
704 .as_ref()
705 .map(|account_details| &account_details.storage_details.header)
706 }
707
708 pub fn storage_details(&self) -> Option<&AccountStorageDetails> {
710 self.state_headers.as_ref().map(|d| &d.storage_details)
711 }
712
713 pub fn vault_details(&self) -> Option<&AccountVaultDetails> {
715 self.state_headers.as_ref().map(|d| &d.vault_details)
716 }
717
718 pub fn find_map_details(
720 &self,
721 slot_name: &StorageSlotName,
722 ) -> Option<&AccountStorageMapDetails> {
723 self.state_headers
724 .as_ref()
725 .and_then(|details| details.storage_details.find_map_details(slot_name))
726 }
727
728 pub fn account_code(&self) -> Option<&AccountCode> {
730 self.state_headers.as_ref().map(|headers| &headers.code)
731 }
732
733 pub fn code_commitment(&self) -> Option<Word> {
735 self.account_code().map(AccountCode::commitment)
736 }
737
738 pub fn account_commitment(&self) -> Word {
740 self.account_witness.state_commitment()
741 }
742
743 pub fn account_witness(&self) -> &AccountWitness {
744 &self.account_witness
745 }
746
747 pub fn merkle_proof(&self) -> &SparseMerklePath {
749 self.account_witness.path()
750 }
751
752 pub fn into_parts(self) -> (AccountWitness, Option<AccountDetails>) {
754 (self.account_witness, self.state_headers)
755 }
756
757 pub fn into_details(self) -> Option<AccountDetails> {
759 self.state_headers
760 }
761
762 pub fn details_mut(&mut self) -> Option<&mut AccountDetails> {
768 self.state_headers.as_mut()
769 }
770}
771
772#[cfg(feature = "tonic")]
773impl TryFrom<proto::rpc::AccountResponse> for AccountProof {
774 type Error = RpcError;
775 fn try_from(account_proof: proto::rpc::AccountResponse) -> Result<Self, Self::Error> {
776 let Some(witness) = account_proof.witness else {
777 return Err(RpcError::ExpectedDataMissing(
778 "GetAccount returned an account without witness".to_string(),
779 ));
780 };
781
782 let details: Option<AccountDetails> = {
783 match account_proof.details {
784 None => None,
785 Some(details) => Some(
786 details
787 .into_domain(&BTreeMap::new(), &AccountStorageRequirements::default())?,
788 ),
789 }
790 };
791 AccountProof::new(witness.try_into()?, details)
792 .map_err(|err| RpcError::InvalidResponse(format!("{err}")))
793 }
794}
795
796impl TryFrom<proto::account::AccountWitness> for AccountWitness {
800 type Error = RpcError;
801
802 fn try_from(account_witness: proto::account::AccountWitness) -> Result<Self, Self::Error> {
803 let state_commitment = account_witness
804 .commitment
805 .ok_or(proto::account::AccountWitness::missing_field(stringify!(state_commitment)))?
806 .try_into()?;
807 let merkle_path = account_witness
808 .path
809 .ok_or(proto::account::AccountWitness::missing_field(stringify!(merkle_path)))?
810 .try_into()?;
811 let account_id = account_witness
812 .witness_id
813 .ok_or(proto::account::AccountWitness::missing_field(stringify!(witness_id)))?
814 .try_into()?;
815
816 let witness = AccountWitness::new(account_id, state_commitment, merkle_path)
817 .map_err(|err| RpcError::InvalidResponse(format!("{err}")))?;
818 Ok(witness)
819 }
820}
821
822#[derive(Clone, Debug, Default, Eq, PartialEq)]
831pub struct AccountStorageRequirements(BTreeMap<StorageSlotName, Vec<StorageMapKey>>);
832
833impl AccountStorageRequirements {
834 pub fn new<'a>(
840 slots_and_keys: impl IntoIterator<
841 Item = (StorageSlotName, impl IntoIterator<Item = &'a StorageMapKey>),
842 >,
843 ) -> Self {
844 let map = slots_and_keys
845 .into_iter()
846 .map(|(slot_name, keys_iter)| {
847 let mut keys_vec: Vec<StorageMapKey> = Vec::new();
848 for key in keys_iter {
849 if !keys_vec.contains(key) {
850 keys_vec.push(*key);
851 }
852 }
853 (slot_name, keys_vec)
854 })
855 .collect();
856
857 AccountStorageRequirements(map)
858 }
859
860 pub fn all_entries(slot_names: &[StorageSlotName]) -> Self {
863 AccountStorageRequirements(
864 slot_names.iter().map(|name| (name.clone(), Vec::new())).collect(),
865 )
866 }
867
868 pub fn inner(&self) -> &BTreeMap<StorageSlotName, Vec<StorageMapKey>> {
869 &self.0
870 }
871
872 pub fn keys_for_slot(&self, slot_name: &StorageSlotName) -> &[StorageMapKey] {
874 self.0.get(slot_name).map_or(&[], Vec::as_slice)
875 }
876}
877
878impl From<AccountStorageRequirements> for Vec<StorageMapDetailRequest> {
879 fn from(value: AccountStorageRequirements) -> Vec<StorageMapDetailRequest> {
880 let request_map = value.0;
881 let mut requests = Vec::with_capacity(request_map.len());
882 for (slot_name, map_keys) in request_map {
883 let slot_data = if map_keys.is_empty() {
884 Some(SlotData::AllEntries(true))
885 } else {
886 let keys = map_keys.into_iter().map(|key| Word::from(key).into()).collect();
887 Some(SlotData::MapKeys(MapKeys { map_keys: keys }))
888 };
889 requests.push(StorageMapDetailRequest {
890 slot_name: slot_name.to_string(),
891 slot_data,
892 });
893 }
894 requests
895 }
896}
897
898impl Serializable for AccountStorageRequirements {
899 fn write_into<W: miden_tx::utils::serde::ByteWriter>(&self, target: &mut W) {
900 target.write(&self.0);
901 }
902}
903
904impl Deserializable for AccountStorageRequirements {
905 fn read_from<R: miden_tx::utils::serde::ByteReader>(
906 source: &mut R,
907 ) -> Result<Self, miden_tx::utils::serde::DeserializationError> {
908 Ok(AccountStorageRequirements(source.read()?))
909 }
910}
911
912#[derive(Clone, Debug, Default)]
917pub enum VaultFetch {
918 #[default]
920 Skip,
921 Always,
923 IfChangedFrom(Word),
929}
930
931impl From<VaultFetch> for Option<proto::primitives::Digest> {
932 fn from(vault: VaultFetch) -> Self {
936 match vault {
937 VaultFetch::Skip => None,
938 VaultFetch::Always => Some(EMPTY_WORD.into()),
939 VaultFetch::IfChangedFrom(commitment) => Some(commitment.into()),
940 }
941 }
942}
943
944#[derive(Clone, Debug, Default)]
950pub enum StorageMapFetch {
951 #[default]
953 Skip,
954 All,
958 Slots(AccountStorageRequirements),
961}
962
963impl From<StorageMapFetch> for Option<StorageRequest> {
964 fn from(storage: StorageMapFetch) -> Self {
965 match storage {
966 StorageMapFetch::Skip => None,
967 StorageMapFetch::All => Some(StorageRequest::AllStorageMaps(true)),
968 StorageMapFetch::Slots(reqs) => {
969 Some(StorageRequest::StorageMaps(StorageMapDetailRequests {
970 storage_maps: reqs.into(),
971 }))
972 },
973 }
974 }
975}
976
977#[derive(Clone, Debug, Default)]
979pub struct GetAccountRequest {
980 pub storage: StorageMapFetch,
982 pub at: AccountStateAt,
984 pub known_code: Option<AccountCode>,
987 pub vault: VaultFetch,
989}
990
991impl GetAccountRequest {
992 #[must_use]
996 pub fn new() -> Self {
997 Self {
998 storage: StorageMapFetch::Skip,
999 at: AccountStateAt::ChainTip,
1000 known_code: None,
1001 vault: VaultFetch::Skip,
1002 }
1003 }
1004
1005 #[must_use]
1007 pub fn with_storage(mut self, storage: StorageMapFetch) -> Self {
1008 self.storage = storage;
1009 self
1010 }
1011
1012 #[must_use]
1014 pub fn at(mut self, at: AccountStateAt) -> Self {
1015 self.at = at;
1016 self
1017 }
1018
1019 #[must_use]
1022 pub fn with_known_code(mut self, known_code: Option<AccountCode>) -> Self {
1023 self.known_code = known_code;
1024 self
1025 }
1026
1027 #[must_use]
1029 pub fn with_vault(mut self, vault: VaultFetch) -> Self {
1030 self.vault = vault;
1031 self
1032 }
1033}
1034
1035#[derive(Debug, Error)]
1039pub enum AccountProofError {
1040 #[error(
1041 "the received account commitment doesn't match the received account header's commitment"
1042 )]
1043 InconsistentAccountCommitment,
1044 #[error("the received account id doesn't match the received account header's id")]
1045 InconsistentAccountId,
1046 #[error(
1047 "the received code commitment doesn't match the received account header's code commitment"
1048 )]
1049 InconsistentCodeCommitment,
1050}