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