Skip to main content

miden_node_proto/domain/
account.rs

1use std::fmt::{Debug, Display, Formatter};
2
3use miden_node_utils::limiter::{QueryParamLimiter, QueryParamStorageMapKeyTotalLimit};
4use miden_protocol::Word;
5use miden_protocol::account::{
6    Account,
7    AccountHeader,
8    AccountId,
9    AccountStorageHeader,
10    StorageMap,
11    StorageMapKey,
12    StorageSlotHeader,
13    StorageSlotName,
14    StorageSlotType,
15};
16use miden_protocol::asset::Asset;
17use miden_protocol::block::BlockNumber;
18use miden_protocol::block::account_tree::AccountWitness;
19use miden_protocol::crypto::merkle::SparseMerklePath;
20use miden_protocol::crypto::merkle::smt::SmtProof;
21use miden_protocol::utils::serde::{Deserializable, DeserializationError, Serializable};
22
23use super::try_convert;
24use crate::decode;
25use crate::decode::{ConversionResultExt, GrpcDecodeExt};
26use crate::errors::ConversionError;
27use crate::generated::{self as proto};
28
29#[cfg(test)]
30mod tests;
31
32// ACCOUNT ID
33// ================================================================================================
34
35impl Display for proto::account::AccountId {
36    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
37        write!(f, "0x")?;
38        for byte in &self.id {
39            write!(f, "{byte:02x}")?;
40        }
41        Ok(())
42    }
43}
44
45impl Debug for proto::account::AccountId {
46    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
47        Display::fmt(self, f)
48    }
49}
50
51// FROM PROTO ACCOUNT ID
52// ------------------------------------------------------------------------------------------------
53
54impl TryFrom<proto::account::AccountId> for AccountId {
55    type Error = ConversionError;
56
57    fn try_from(account_id: proto::account::AccountId) -> Result<Self, Self::Error> {
58        AccountId::read_from_bytes(&account_id.id)
59            .map_err(|_| ConversionError::message("value is not in the range 0..MODULUS"))
60    }
61}
62
63// INTO PROTO ACCOUNT ID
64// ------------------------------------------------------------------------------------------------
65
66impl From<&AccountId> for proto::account::AccountId {
67    fn from(account_id: &AccountId) -> Self {
68        (*account_id).into()
69    }
70}
71
72impl From<AccountId> for proto::account::AccountId {
73    fn from(account_id: AccountId) -> Self {
74        Self { id: account_id.to_bytes() }
75    }
76}
77
78// ACCOUNT UPDATE
79// ================================================================================================
80
81#[derive(Debug, PartialEq)]
82pub struct AccountSummary {
83    pub account_id: AccountId,
84    pub account_commitment: Word,
85    pub block_num: BlockNumber,
86}
87
88impl From<&AccountSummary> for proto::account::AccountSummary {
89    fn from(update: &AccountSummary) -> Self {
90        Self {
91            account_id: Some(update.account_id.into()),
92            account_commitment: Some(update.account_commitment.into()),
93            block_num: update.block_num.as_u32(),
94        }
95    }
96}
97
98#[derive(Debug, PartialEq)]
99pub struct AccountInfo {
100    pub summary: AccountSummary,
101    pub details: Option<Account>,
102}
103
104impl From<&AccountInfo> for proto::account::AccountDetails {
105    fn from(AccountInfo { summary, details }: &AccountInfo) -> Self {
106        Self {
107            summary: Some(summary.into()),
108            details: details.as_ref().map(Serializable::to_bytes),
109        }
110    }
111}
112
113// ACCOUNT STORAGE HEADER
114//================================================================================================
115
116impl TryFrom<proto::account::AccountStorageHeader> for AccountStorageHeader {
117    type Error = ConversionError;
118
119    fn try_from(value: proto::account::AccountStorageHeader) -> Result<Self, Self::Error> {
120        let proto::account::AccountStorageHeader { slots } = value;
121
122        let slot_headers = slots
123            .into_iter()
124            .map(|slot| {
125                let decoder = slot.decoder();
126                let slot_name = StorageSlotName::new(slot.slot_name)?;
127                let slot_type = storage_slot_type_from_raw(slot.slot_type)?;
128                let commitment = decode!(decoder, slot.commitment)?;
129                Ok(StorageSlotHeader::new(slot_name, slot_type, commitment))
130            })
131            .collect::<Result<Vec<_>, ConversionError>>()
132            .context("slots")?;
133
134        Ok(AccountStorageHeader::new(slot_headers)?)
135    }
136}
137
138// ACCOUNT REQUEST
139// ================================================================================================
140
141/// Represents a request for an account proof.
142#[derive(Debug)]
143pub struct AccountRequest {
144    pub account_id: AccountId,
145    // If not present, the latest account proof references the latest available
146    pub block_num: Option<BlockNumber>,
147    pub details: Option<AccountDetailRequest>,
148}
149
150impl TryFrom<proto::rpc::AccountRequest> for AccountRequest {
151    type Error = ConversionError;
152
153    fn try_from(value: proto::rpc::AccountRequest) -> Result<Self, Self::Error> {
154        let decoder = value.decoder();
155        let proto::rpc::AccountRequest { account_id, block_num, details } = value;
156
157        let account_id = decode!(decoder, account_id)?;
158        let block_num = block_num.map(Into::into);
159
160        let details = details.map(TryFrom::try_from).transpose().context("details")?;
161
162        Ok(AccountRequest { account_id, block_num, details })
163    }
164}
165
166/// Represents a request for account details alongside specific storage data.
167#[derive(Debug)]
168pub struct AccountDetailRequest {
169    pub code_commitment: Option<Word>,
170    pub asset_vault_commitment: Option<Word>,
171    pub storage_request: AccountStorageRequest,
172}
173
174#[derive(Debug, Clone, PartialEq, Eq)]
175pub enum AccountStorageRequest {
176    None,
177    AllStorageMaps,
178    Explicit(Vec<StorageMapRequest>),
179}
180
181impl TryFrom<proto::rpc::account_request::AccountDetailRequest> for AccountDetailRequest {
182    type Error = ConversionError;
183
184    fn try_from(
185        value: proto::rpc::account_request::AccountDetailRequest,
186    ) -> Result<Self, Self::Error> {
187        use proto::rpc::account_request::account_detail_request::StorageRequest as ProtoStorageRequest;
188
189        let proto::rpc::account_request::AccountDetailRequest {
190            code_commitment,
191            asset_vault_commitment,
192            storage_request,
193        } = value;
194
195        let code_commitment =
196            code_commitment.map(TryFrom::try_from).transpose().context("code_commitment")?;
197        let asset_vault_commitment = asset_vault_commitment
198            .map(TryFrom::try_from)
199            .transpose()
200            .context("asset_vault_commitment")?;
201
202        let storage_request = match storage_request {
203            None => AccountStorageRequest::None,
204            Some(ProtoStorageRequest::AllStorageMaps(true)) => {
205                AccountStorageRequest::AllStorageMaps
206            },
207            Some(ProtoStorageRequest::AllStorageMaps(false)) => {
208                return Err(ConversionError::message("all_storage_maps must be true when set"));
209            },
210            Some(ProtoStorageRequest::StorageMaps(requests)) => {
211                let requests = try_convert(requests.storage_maps)
212                    .collect::<Result<_, _>>()
213                    .context("storage_maps")?;
214                AccountStorageRequest::Explicit(requests)
215            },
216        };
217
218        Ok(AccountDetailRequest {
219            code_commitment,
220            asset_vault_commitment,
221            storage_request,
222        })
223    }
224}
225
226#[derive(Debug, Clone, PartialEq, Eq)]
227pub struct StorageMapRequest {
228    pub slot_name: StorageSlotName,
229    pub slot_data: SlotData,
230}
231
232impl TryFrom<proto::rpc::account_request::account_detail_request::StorageMapDetailRequest>
233    for StorageMapRequest
234{
235    type Error = ConversionError;
236
237    fn try_from(
238        value: proto::rpc::account_request::account_detail_request::StorageMapDetailRequest,
239    ) -> Result<Self, Self::Error> {
240        let decoder = value.decoder();
241        let proto::rpc::account_request::account_detail_request::StorageMapDetailRequest {
242            slot_name,
243            slot_data,
244        } = value;
245
246        let slot_name = StorageSlotName::new(slot_name).context("slot_name")?;
247        let slot_data = decode!(decoder, slot_data)?;
248
249        Ok(StorageMapRequest { slot_name, slot_data })
250    }
251}
252
253/// Request of slot data values.
254#[derive(Debug, Clone, PartialEq, Eq)]
255pub enum SlotData {
256    All,
257    MapKeys(Vec<StorageMapKey>),
258}
259
260impl
261    TryFrom<
262        proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData,
263    > for SlotData
264{
265    type Error = ConversionError;
266
267    fn try_from(
268        value: proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData,
269    ) -> Result<Self, Self::Error> {
270        use proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData as ProtoSlotData;
271
272        Ok(match value {
273            ProtoSlotData::AllEntries(true) => SlotData::All,
274            ProtoSlotData::AllEntries(false) => {
275                return Err(ConversionError::message("enum variant discriminant out of range"));
276            },
277            ProtoSlotData::MapKeys(keys) => {
278                let keys = try_convert(keys.map_keys).collect::<Result<Vec<_>, _>>()?;
279                SlotData::MapKeys(keys)
280            },
281        })
282    }
283}
284
285// ACCOUNT HEADER CONVERSIONS
286//================================================================================================
287
288impl TryFrom<proto::account::AccountHeader> for AccountHeader {
289    type Error = ConversionError;
290
291    fn try_from(value: proto::account::AccountHeader) -> Result<Self, Self::Error> {
292        let decoder = value.decoder();
293        let proto::account::AccountHeader {
294            account_id,
295            vault_root,
296            storage_commitment,
297            code_commitment,
298            nonce,
299        } = value;
300
301        let account_id = decode!(decoder, account_id)?;
302        let vault_root = decode!(decoder, vault_root)?;
303        let storage_commitment = decode!(decoder, storage_commitment)?;
304        let code_commitment = decode!(decoder, code_commitment)?;
305        let nonce = nonce
306            .try_into()
307            .map_err(|e| ConversionError::message(format!("{e}")))
308            .context("nonce")?;
309
310        Ok(AccountHeader::new(
311            account_id,
312            nonce,
313            vault_root,
314            storage_commitment,
315            code_commitment,
316        ))
317    }
318}
319
320impl From<AccountHeader> for proto::account::AccountHeader {
321    fn from(header: AccountHeader) -> Self {
322        proto::account::AccountHeader {
323            account_id: Some(header.id().into()),
324            vault_root: Some(header.vault_root().into()),
325            storage_commitment: Some(header.storage_commitment().into()),
326            code_commitment: Some(header.code_commitment().into()),
327            nonce: header.nonce().as_canonical_u64(),
328        }
329    }
330}
331
332impl From<AccountStorageHeader> for proto::account::AccountStorageHeader {
333    fn from(value: AccountStorageHeader) -> Self {
334        let slots = value
335            .slots()
336            .map(|slot_header| proto::account::account_storage_header::StorageSlot {
337                slot_name: slot_header.name().to_string(),
338                slot_type: storage_slot_type_to_raw(slot_header.slot_type()),
339                commitment: Some(proto::primitives::Digest::from(slot_header.value())),
340            })
341            .collect();
342
343        Self { slots }
344    }
345}
346
347// ACCOUNT VAULT DETAILS
348//================================================================================================
349
350/// Account vault details
351///
352/// When an account contains a large number of assets (>
353/// [`AccountVaultDetails::MAX_RETURN_ENTRIES`]), including all assets in a single RPC response
354/// creates performance issues. In such cases, the `LimitExceeded` variant indicates to the client
355/// to use the `SyncAccountVault` endpoint instead.
356#[derive(Debug, Clone, PartialEq, Eq)]
357pub enum AccountVaultDetails {
358    /// The vault has too many assets to return inline. Clients must use `SyncAccountVault` endpoint
359    /// instead.
360    LimitExceeded,
361
362    /// The assets in the vault (up to `MAX_RETURN_ENTRIES`).
363    Assets(Vec<Asset>),
364}
365
366impl AccountVaultDetails {
367    /// Maximum number of vault entries that can be returned in a single response. Accounts with
368    /// more assets will have `LimitExceeded` variant.
369    pub const MAX_RETURN_ENTRIES: usize = 1000;
370
371    pub fn empty() -> Self {
372        Self::Assets(Vec::new())
373    }
374
375    /// Creates `AccountVaultDetails` from a list of assets.
376    pub fn from_assets(assets: Vec<Asset>) -> Self {
377        if assets.len() > Self::MAX_RETURN_ENTRIES {
378            Self::LimitExceeded
379        } else {
380            Self::Assets(assets)
381        }
382    }
383}
384
385impl TryFrom<proto::rpc::AccountVaultDetails> for AccountVaultDetails {
386    type Error = ConversionError;
387
388    fn try_from(value: proto::rpc::AccountVaultDetails) -> Result<Self, Self::Error> {
389        let proto::rpc::AccountVaultDetails { too_many_assets, assets } = value;
390
391        if too_many_assets {
392            Ok(Self::LimitExceeded)
393        } else {
394            let parsed_assets = Result::<Vec<_>, ConversionError>::from_iter(
395                assets.into_iter().map(Asset::try_from),
396            )?;
397            Ok(Self::Assets(parsed_assets))
398        }
399    }
400}
401
402impl From<AccountVaultDetails> for proto::rpc::AccountVaultDetails {
403    fn from(value: AccountVaultDetails) -> Self {
404        match value {
405            AccountVaultDetails::LimitExceeded => Self {
406                too_many_assets: true,
407                assets: Vec::new(),
408            },
409            AccountVaultDetails::Assets(assets) => Self {
410                too_many_assets: false,
411                assets: Vec::from_iter(assets.into_iter().map(proto::primitives::Asset::from)),
412            },
413        }
414    }
415}
416
417// ACCOUNT STORAGE MAP DETAILS
418//================================================================================================
419
420/// Details about an account storage map slot.
421#[derive(Debug, Clone, PartialEq, Eq)]
422pub struct AccountStorageMapDetails {
423    pub slot_name: StorageSlotName,
424    pub entries: StorageMapEntries,
425}
426
427/// Storage map entries for an account storage slot.
428///
429/// When a storage map contains many entries (> [`AccountStorageMapDetails::MAX_RETURN_ENTRIES`]),
430/// returning all entries in a single RPC response creates performance issues. In such cases,
431/// the `LimitExceeded` variant indicates to the client to use the `SyncAccountStorageMaps` endpoint
432/// instead.
433#[derive(Debug, Clone, PartialEq, Eq)]
434pub enum StorageMapEntries {
435    /// The map has too many entries to return inline. Clients must use `SyncAccountStorageMaps`
436    /// endpoint instead.
437    LimitExceeded,
438
439    /// All storage map entries (key-value pairs) without proofs. Used when all entries are
440    /// requested for small maps.
441    AllEntries(Vec<(StorageMapKey, Word)>),
442
443    /// Specific entries with their SMT proofs for client-side verification. Used when specific keys
444    /// are requested from the storage map.
445    EntriesWithProofs(Vec<SmtProof>),
446}
447
448impl AccountStorageMapDetails {
449    /// Maximum number of storage map entries that can be returned in a single response.
450    pub const MAX_RETURN_ENTRIES: usize = 1000;
451
452    /// Maximum number of SMT proofs that can be returned in a single response.
453    ///
454    /// This limit is more restrictive than [`Self::MAX_RETURN_ENTRIES`] because SMT proofs
455    /// are larger (up to 64 inner nodes each) and more CPU-intensive to generate.
456    ///
457    /// This is defined by [`QueryParamStorageMapKeyTotalLimit::LIMIT`] and used both in RPC
458    /// validation and store-level enforcement to ensure consistent limits.
459    pub const MAX_SMT_PROOF_ENTRIES: usize = QueryParamStorageMapKeyTotalLimit::LIMIT;
460
461    /// Creates storage map details with all entries from the storage map.
462    ///
463    /// If the storage map has too many entries (> `MAX_RETURN_ENTRIES`),
464    /// returns `LimitExceeded` variant.
465    pub fn from_all_entries(slot_name: StorageSlotName, storage_map: &StorageMap) -> Self {
466        if storage_map.num_entries() > Self::MAX_RETURN_ENTRIES {
467            Self {
468                slot_name,
469                entries: StorageMapEntries::LimitExceeded,
470            }
471        } else {
472            let entries = Vec::from_iter(storage_map.entries().map(|(k, v)| (*k, *v)));
473            Self {
474                slot_name,
475                entries: StorageMapEntries::AllEntries(entries),
476            }
477        }
478    }
479
480    /// Creates storage map details from forest-queried entries.
481    ///
482    /// Returns `LimitExceeded` if too many entries.
483    pub fn from_forest_entries(
484        slot_name: StorageSlotName,
485        entries: Vec<(StorageMapKey, Word)>,
486    ) -> Self {
487        if entries.len() > Self::MAX_RETURN_ENTRIES {
488            Self {
489                slot_name,
490                entries: StorageMapEntries::LimitExceeded,
491            }
492        } else {
493            Self {
494                slot_name,
495                entries: StorageMapEntries::AllEntries(entries),
496            }
497        }
498    }
499
500    /// Creates storage map details from pre-computed SMT proofs.
501    ///
502    /// Use this when the caller has already obtained the proofs from an `SmtForest`.
503    /// Returns `LimitExceeded` if too many proofs are provided.
504    pub fn from_proofs(slot_name: StorageSlotName, proofs: Vec<SmtProof>) -> Self {
505        if proofs.len() > Self::MAX_SMT_PROOF_ENTRIES {
506            Self {
507                slot_name,
508                entries: StorageMapEntries::LimitExceeded,
509            }
510        } else {
511            Self {
512                slot_name,
513                entries: StorageMapEntries::EntriesWithProofs(proofs),
514            }
515        }
516    }
517
518    /// Creates storage map details indicating the limit was exceeded.
519    pub fn limit_exceeded(slot_name: StorageSlotName) -> Self {
520        Self {
521            slot_name,
522            entries: StorageMapEntries::LimitExceeded,
523        }
524    }
525}
526
527impl TryFrom<proto::rpc::account_storage_details::AccountStorageMapDetails>
528    for AccountStorageMapDetails
529{
530    type Error = ConversionError;
531
532    fn try_from(
533        value: proto::rpc::account_storage_details::AccountStorageMapDetails,
534    ) -> Result<Self, Self::Error> {
535        use proto::rpc::account_storage_details::account_storage_map_details::{
536            AllMapEntries,
537            Entries as ProtoEntries,
538            MapEntriesWithProofs,
539        };
540
541        let proto::rpc::account_storage_details::AccountStorageMapDetails {
542            slot_name,
543            too_many_entries,
544            entries,
545        } = value;
546
547        let slot_name = StorageSlotName::new(slot_name).context("slot_name")?;
548
549        let entries = if too_many_entries {
550            StorageMapEntries::LimitExceeded
551        } else {
552            match entries {
553                None => {
554                    return Err(ConversionError::missing_field::<
555                        proto::rpc::account_storage_details::AccountStorageMapDetails,
556                    >("entries"));
557                },
558                Some(ProtoEntries::AllEntries(AllMapEntries { entries })) => {
559                    let entries = entries
560                        .into_iter()
561                        .map(|entry| {
562                            let decoder = entry.decoder();
563                            let key = StorageMapKey::new(decode!(decoder, entry.key)?);
564                            let value = decode!(decoder, entry.value)?;
565                            Ok((key, value))
566                        })
567                        .collect::<Result<Vec<_>, ConversionError>>()
568                        .context("entries")?;
569                    StorageMapEntries::AllEntries(entries)
570                },
571                Some(ProtoEntries::EntriesWithProofs(MapEntriesWithProofs { entries })) => {
572                    let proofs = entries
573                        .into_iter()
574                        .map(|entry| {
575                            let decoder = entry.decoder();
576                            decode!(decoder, entry.proof)
577                        })
578                        .collect::<Result<Vec<_>, ConversionError>>()
579                        .context("entries")?;
580                    StorageMapEntries::EntriesWithProofs(proofs)
581                },
582            }
583        };
584
585        Ok(Self { slot_name, entries })
586    }
587}
588
589impl From<AccountStorageMapDetails>
590    for proto::rpc::account_storage_details::AccountStorageMapDetails
591{
592    fn from(value: AccountStorageMapDetails) -> Self {
593        use proto::rpc::account_storage_details::account_storage_map_details::{
594            AllMapEntries,
595            Entries as ProtoEntries,
596            MapEntriesWithProofs,
597        };
598
599        let AccountStorageMapDetails { slot_name, entries } = value;
600
601        let (too_many_entries, proto_entries) = match entries {
602            StorageMapEntries::LimitExceeded => (true, None),
603            StorageMapEntries::AllEntries(entries) => {
604                let all = AllMapEntries {
605                    entries: Vec::from_iter(entries.into_iter().map(|(key, value)| {
606                        proto::rpc::account_storage_details::account_storage_map_details::all_map_entries::StorageMapEntry {
607                            key: Some(key.into()),
608                            value: Some(value.into()),
609                        }
610                    })),
611                };
612                (false, Some(ProtoEntries::AllEntries(all)))
613            },
614            StorageMapEntries::EntriesWithProofs(proofs) => {
615                use miden_protocol::crypto::merkle::smt::SmtLeaf;
616
617                let with_proofs = MapEntriesWithProofs {
618                    entries: Vec::from_iter(proofs.into_iter().map(|proof| {
619                        // Get key/value from the leaf before consuming the proof
620                        let (key, value) = match proof.leaf() {
621                            SmtLeaf::Empty(_) => {
622                                (miden_protocol::EMPTY_WORD, miden_protocol::EMPTY_WORD)
623                            },
624                            SmtLeaf::Single((k, v)) => (*k, *v),
625                            SmtLeaf::Multiple(entries) => entries.iter().next().map_or(
626                                (miden_protocol::EMPTY_WORD, miden_protocol::EMPTY_WORD),
627                                |(k, v)| (*k, *v),
628                            ),
629                        };
630                        let smt_opening = proto::primitives::SmtOpening::from(proof);
631                        proto::rpc::account_storage_details::account_storage_map_details::map_entries_with_proofs::StorageMapEntryWithProof {
632                            key: Some(key.into()),
633                            value: Some(value.into()),
634                            proof: Some(smt_opening),
635                        }
636                    })),
637                };
638                (false, Some(ProtoEntries::EntriesWithProofs(with_proofs)))
639            },
640        };
641
642        Self {
643            slot_name: slot_name.to_string(),
644            too_many_entries,
645            entries: proto_entries,
646        }
647    }
648}
649
650#[derive(Debug, Clone, PartialEq)]
651pub struct AccountStorageDetails {
652    pub header: AccountStorageHeader,
653    pub map_details: Vec<AccountStorageMapDetails>,
654}
655
656impl AccountStorageDetails {
657    /// Creates storage details where all map slots indicate limit exceeded.
658    pub fn all_limits_exceeded(
659        header: AccountStorageHeader,
660        slot_names: impl IntoIterator<Item = StorageSlotName>,
661    ) -> Self {
662        Self {
663            header,
664            map_details: Vec::from_iter(
665                slot_names.into_iter().map(AccountStorageMapDetails::limit_exceeded),
666            ),
667        }
668    }
669}
670
671impl TryFrom<proto::rpc::AccountStorageDetails> for AccountStorageDetails {
672    type Error = ConversionError;
673
674    fn try_from(value: proto::rpc::AccountStorageDetails) -> Result<Self, Self::Error> {
675        let decoder = value.decoder();
676        let proto::rpc::AccountStorageDetails { header, map_details } = value;
677
678        let header = decode!(decoder, header)?;
679
680        let map_details =
681            try_convert(map_details).collect::<Result<Vec<_>, _>>().context("map_details")?;
682
683        Ok(Self { header, map_details })
684    }
685}
686
687impl From<AccountStorageDetails> for proto::rpc::AccountStorageDetails {
688    fn from(value: AccountStorageDetails) -> Self {
689        let AccountStorageDetails { header, map_details } = value;
690
691        Self {
692            header: Some(header.into()),
693            map_details: map_details.into_iter().map(Into::into).collect(),
694        }
695    }
696}
697
698fn storage_slot_type_from_raw(slot_type: u32) -> Result<StorageSlotType, ConversionError> {
699    Ok(match slot_type {
700        0 => StorageSlotType::Value,
701        1 => StorageSlotType::Map,
702        _ => {
703            return Err(ConversionError::message("enum variant discriminant out of range"));
704        },
705    })
706}
707
708const fn storage_slot_type_to_raw(slot_type: StorageSlotType) -> u32 {
709    match slot_type {
710        StorageSlotType::Value => 0,
711        StorageSlotType::Map => 1,
712    }
713}
714
715// ACCOUNT PROOF RESPONSE
716//================================================================================================
717
718/// Represents the response to an account proof request.
719pub struct AccountResponse {
720    pub block_num: BlockNumber,
721    pub witness: AccountWitness,
722    pub details: Option<AccountDetails>,
723}
724
725impl TryFrom<proto::rpc::AccountResponse> for AccountResponse {
726    type Error = ConversionError;
727
728    fn try_from(value: proto::rpc::AccountResponse) -> Result<Self, Self::Error> {
729        let decoder = value.decoder();
730        let proto::rpc::AccountResponse { block_num, witness, details } = value;
731
732        let block_num = block_num
733            .ok_or(ConversionError::missing_field::<proto::rpc::AccountResponse>("block_num"))?
734            .into();
735
736        let witness = decode!(decoder, witness)?;
737
738        let details = details.map(TryFrom::try_from).transpose().context("details")?;
739
740        Ok(AccountResponse { block_num, witness, details })
741    }
742}
743
744impl From<AccountResponse> for proto::rpc::AccountResponse {
745    fn from(value: AccountResponse) -> Self {
746        let AccountResponse { block_num, witness, details } = value;
747
748        Self {
749            witness: Some(witness.into()),
750            details: details.map(Into::into),
751            block_num: Some(block_num.into()),
752        }
753    }
754}
755
756// ACCOUNT DETAILS
757//================================================================================================
758
759/// Represents account details returned in response to an account proof request.
760pub struct AccountDetails {
761    pub account_header: AccountHeader,
762    pub account_code: Option<Vec<u8>>,
763    pub vault_details: AccountVaultDetails,
764    pub storage_details: AccountStorageDetails,
765}
766
767impl AccountDetails {
768    /// Creates account details where all storage map slots indicate limit exceeded.
769    pub fn with_storage_limits_exceeded(
770        account_header: AccountHeader,
771        account_code: Option<Vec<u8>>,
772        vault_details: AccountVaultDetails,
773        storage_header: AccountStorageHeader,
774        slot_names: impl IntoIterator<Item = StorageSlotName>,
775    ) -> Self {
776        Self {
777            account_header,
778            account_code,
779            vault_details,
780            storage_details: AccountStorageDetails::all_limits_exceeded(storage_header, slot_names),
781        }
782    }
783}
784
785impl TryFrom<proto::rpc::account_response::AccountDetails> for AccountDetails {
786    type Error = ConversionError;
787
788    fn try_from(value: proto::rpc::account_response::AccountDetails) -> Result<Self, Self::Error> {
789        let decoder = value.decoder();
790        let proto::rpc::account_response::AccountDetails {
791            header,
792            code,
793            vault_details,
794            storage_details,
795        } = value;
796
797        let account_header = decode!(decoder, header)?;
798
799        let storage_details = decode!(decoder, storage_details)?;
800
801        let vault_details = decode!(decoder, vault_details)?;
802        let account_code = code;
803
804        Ok(AccountDetails {
805            account_header,
806            account_code,
807            vault_details,
808            storage_details,
809        })
810    }
811}
812
813impl From<AccountDetails> for proto::rpc::account_response::AccountDetails {
814    fn from(value: AccountDetails) -> Self {
815        let AccountDetails {
816            account_header,
817            storage_details,
818            account_code,
819            vault_details,
820        } = value;
821
822        let header = Some(proto::account::AccountHeader::from(account_header));
823        let storage_details = Some(storage_details.into());
824        let code = account_code;
825        let vault_details = Some(vault_details.into());
826
827        Self {
828            header,
829            storage_details,
830            code,
831            vault_details,
832        }
833    }
834}
835
836// ACCOUNT WITNESS
837// ================================================================================================
838
839impl TryFrom<proto::account::AccountWitness> for AccountWitness {
840    type Error = ConversionError;
841
842    fn try_from(account_witness: proto::account::AccountWitness) -> Result<Self, Self::Error> {
843        let decoder = account_witness.decoder();
844        let witness_id = decode!(decoder, account_witness.witness_id)?;
845        let commitment = decode!(decoder, account_witness.commitment)?;
846        let path = decode!(decoder, account_witness.path)?;
847
848        AccountWitness::new(witness_id, commitment, path).map_err(|err| {
849            ConversionError::deserialization(
850                "AccountWitness",
851                DeserializationError::InvalidValue(err.to_string()),
852            )
853        })
854    }
855}
856
857impl From<AccountWitness> for proto::account::AccountWitness {
858    fn from(witness: AccountWitness) -> Self {
859        Self {
860            account_id: Some(witness.id().into()),
861            witness_id: Some(witness.id().into()),
862            commitment: Some(witness.state_commitment().into()),
863            path: Some(witness.into_proof().into_parts().0.into()),
864        }
865    }
866}
867
868// ACCOUNT WITNESS RECORD
869// ================================================================================================
870
871#[derive(Clone, Debug, PartialEq, Eq)]
872pub struct AccountWitnessRecord {
873    pub account_id: AccountId,
874    pub witness: AccountWitness,
875}
876
877impl TryFrom<proto::account::AccountWitness> for AccountWitnessRecord {
878    type Error = ConversionError;
879
880    fn try_from(
881        account_witness_record: proto::account::AccountWitness,
882    ) -> Result<Self, Self::Error> {
883        let decoder = account_witness_record.decoder();
884        let witness_id = decode!(decoder, account_witness_record.witness_id)?;
885        let commitment = decode!(decoder, account_witness_record.commitment)?;
886        let account_id = decode!(decoder, account_witness_record.account_id)?;
887        let path: SparseMerklePath = decode!(decoder, account_witness_record.path)?;
888
889        let witness = AccountWitness::new(witness_id, commitment, path).map_err(|err| {
890            ConversionError::deserialization(
891                "AccountWitness",
892                DeserializationError::InvalidValue(err.to_string()),
893            )
894        })?;
895
896        Ok(Self { account_id, witness })
897    }
898}
899
900impl From<AccountWitnessRecord> for proto::account::AccountWitness {
901    fn from(from: AccountWitnessRecord) -> Self {
902        Self {
903            account_id: Some(from.account_id.into()),
904            witness_id: Some(from.witness.id().into()),
905            commitment: Some(from.witness.state_commitment().into()),
906            path: Some(from.witness.path().clone().into()),
907        }
908    }
909}
910
911// ASSET
912// ================================================================================================
913
914impl TryFrom<proto::primitives::Asset> for Asset {
915    type Error = ConversionError;
916
917    fn try_from(asset: proto::primitives::Asset) -> Result<Self, Self::Error> {
918        let decoder = asset.decoder();
919        let key_word: Word = decode!(decoder, asset.key)?;
920        let value_word: Word = decode!(decoder, asset.value)?;
921
922        let asset = Asset::from_key_value_words(key_word, value_word)?;
923        Ok(asset)
924    }
925}
926
927impl From<Asset> for proto::primitives::Asset {
928    fn from(asset_from: Asset) -> Self {
929        proto::primitives::Asset {
930            key: Some(asset_from.to_key_word().into()),
931            value: Some(asset_from.to_value_word().into()),
932        }
933    }
934}