1use std::fmt::{Debug, Display, Formatter};
2
3use miden_node_utils::formatting::format_opt;
4use miden_node_utils::limiter::{QueryParamLimiter, QueryParamStorageMapKeyTotalLimit};
5use miden_protocol::Word;
6use miden_protocol::account::{
7 Account,
8 AccountHeader,
9 AccountId,
10 AccountStorageHeader,
11 StorageMap,
12 StorageMapKey,
13 StorageSlotHeader,
14 StorageSlotName,
15 StorageSlotType,
16};
17use miden_protocol::asset::{Asset, AssetVault};
18use miden_protocol::block::BlockNumber;
19use miden_protocol::block::account_tree::AccountWitness;
20use miden_protocol::crypto::merkle::SparseMerklePath;
21use miden_protocol::crypto::merkle::smt::SmtProof;
22use miden_protocol::note::NoteAttachment;
23use miden_protocol::utils::{Deserializable, DeserializationError, Serializable};
24use miden_standards::note::{NetworkAccountTarget, NetworkAccountTargetError};
25use thiserror::Error;
26
27use super::try_convert;
28use crate::errors::{ConversionError, MissingFieldHelper};
29use crate::generated::{self as proto};
30
31#[cfg(test)]
32mod tests;
33
34impl Display for proto::account::AccountId {
38 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
39 write!(f, "0x")?;
40 for byte in &self.id {
41 write!(f, "{byte:02x}")?;
42 }
43 Ok(())
44 }
45}
46
47impl Debug for proto::account::AccountId {
48 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
49 Display::fmt(self, f)
50 }
51}
52
53impl TryFrom<proto::account::AccountId> for AccountId {
57 type Error = ConversionError;
58
59 fn try_from(account_id: proto::account::AccountId) -> Result<Self, Self::Error> {
60 AccountId::read_from_bytes(&account_id.id).map_err(|_| ConversionError::NotAValidFelt)
61 }
62}
63
64impl From<&AccountId> for proto::account::AccountId {
68 fn from(account_id: &AccountId) -> Self {
69 (*account_id).into()
70 }
71}
72
73impl From<AccountId> for proto::account::AccountId {
74 fn from(account_id: AccountId) -> Self {
75 Self { id: account_id.to_bytes() }
76 }
77}
78
79#[derive(Debug, PartialEq)]
83pub struct AccountSummary {
84 pub account_id: AccountId,
85 pub account_commitment: Word,
86 pub block_num: BlockNumber,
87}
88
89impl From<&AccountSummary> for proto::account::AccountSummary {
90 fn from(update: &AccountSummary) -> Self {
91 Self {
92 account_id: Some(update.account_id.into()),
93 account_commitment: Some(update.account_commitment.into()),
94 block_num: update.block_num.as_u32(),
95 }
96 }
97}
98
99#[derive(Debug, PartialEq)]
100pub struct AccountInfo {
101 pub summary: AccountSummary,
102 pub details: Option<Account>,
103}
104
105impl From<&AccountInfo> for proto::account::AccountDetails {
106 fn from(AccountInfo { summary, details }: &AccountInfo) -> Self {
107 Self {
108 summary: Some(summary.into()),
109 details: details.as_ref().map(Serializable::to_bytes),
110 }
111 }
112}
113
114impl TryFrom<proto::account::AccountStorageHeader> for AccountStorageHeader {
118 type Error = ConversionError;
119
120 fn try_from(value: proto::account::AccountStorageHeader) -> Result<Self, Self::Error> {
121 let proto::account::AccountStorageHeader { slots } = value;
122
123 let slot_headers = slots
124 .into_iter()
125 .map(|slot| {
126 let slot_name = StorageSlotName::new(slot.slot_name)?;
127 let slot_type = storage_slot_type_from_raw(slot.slot_type)?;
128 let commitment =
129 slot.commitment.ok_or(ConversionError::NotAValidFelt)?.try_into()?;
130 Ok(StorageSlotHeader::new(slot_name, slot_type, commitment))
131 })
132 .collect::<Result<Vec<_>, ConversionError>>()?;
133
134 Ok(AccountStorageHeader::new(slot_headers)?)
135 }
136}
137
138pub struct AccountRequest {
143 pub account_id: AccountId,
144 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 proto::rpc::AccountRequest { account_id, block_num, details } = value;
154
155 let account_id = account_id
156 .ok_or(proto::rpc::AccountRequest::missing_field(stringify!(account_id)))?
157 .try_into()?;
158 let block_num = block_num.map(Into::into);
159
160 let details = details.map(TryFrom::try_from).transpose()?;
161
162 Ok(AccountRequest { account_id, block_num, details })
163 }
164}
165
166pub struct AccountDetailRequest {
168 pub code_commitment: Option<Word>,
169 pub asset_vault_commitment: Option<Word>,
170 pub storage_requests: Vec<StorageMapRequest>,
171}
172
173impl TryFrom<proto::rpc::account_request::AccountDetailRequest> for AccountDetailRequest {
174 type Error = ConversionError;
175
176 fn try_from(
177 value: proto::rpc::account_request::AccountDetailRequest,
178 ) -> Result<Self, Self::Error> {
179 let proto::rpc::account_request::AccountDetailRequest {
180 code_commitment,
181 asset_vault_commitment,
182 storage_maps,
183 } = value;
184
185 let code_commitment = code_commitment.map(TryFrom::try_from).transpose()?;
186 let asset_vault_commitment = asset_vault_commitment.map(TryFrom::try_from).transpose()?;
187 let storage_requests = try_convert(storage_maps).collect::<Result<_, _>>()?;
188
189 Ok(AccountDetailRequest {
190 code_commitment,
191 asset_vault_commitment,
192 storage_requests,
193 })
194 }
195}
196
197#[derive(Debug, Clone, PartialEq, Eq)]
198pub struct StorageMapRequest {
199 pub slot_name: StorageSlotName,
200 pub slot_data: SlotData,
201}
202
203impl TryFrom<proto::rpc::account_request::account_detail_request::StorageMapDetailRequest>
204 for StorageMapRequest
205{
206 type Error = ConversionError;
207
208 fn try_from(
209 value: proto::rpc::account_request::account_detail_request::StorageMapDetailRequest,
210 ) -> Result<Self, Self::Error> {
211 let proto::rpc::account_request::account_detail_request::StorageMapDetailRequest {
212 slot_name,
213 slot_data,
214 } = value;
215
216 let slot_name = StorageSlotName::new(slot_name)?;
217 let slot_data = slot_data.ok_or(proto::rpc::account_request::account_detail_request::StorageMapDetailRequest::missing_field(stringify!(slot_data)))?.try_into()?;
218
219 Ok(StorageMapRequest { slot_name, slot_data })
220 }
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
225pub enum SlotData {
226 All,
227 MapKeys(Vec<StorageMapKey>),
228}
229
230impl
231 TryFrom<
232 proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData,
233 > for SlotData
234{
235 type Error = ConversionError;
236
237 fn try_from(
238 value: proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData,
239 ) -> Result<Self, Self::Error> {
240 use proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData as ProtoSlotData;
241
242 Ok(match value {
243 ProtoSlotData::AllEntries(true) => SlotData::All,
244 ProtoSlotData::AllEntries(false) => {
245 return Err(ConversionError::EnumDiscriminantOutOfRange);
246 },
247 ProtoSlotData::MapKeys(keys) => {
248 let keys = try_convert(keys.map_keys).collect::<Result<Vec<_>, _>>()?;
249 SlotData::MapKeys(keys)
250 },
251 })
252 }
253}
254
255impl TryFrom<proto::account::AccountHeader> for AccountHeader {
259 type Error = ConversionError;
260
261 fn try_from(value: proto::account::AccountHeader) -> Result<Self, Self::Error> {
262 let proto::account::AccountHeader {
263 account_id,
264 vault_root,
265 storage_commitment,
266 code_commitment,
267 nonce,
268 } = value;
269
270 let account_id = account_id
271 .ok_or(proto::account::AccountHeader::missing_field(stringify!(account_id)))?
272 .try_into()?;
273 let vault_root = vault_root
274 .ok_or(proto::account::AccountHeader::missing_field(stringify!(vault_root)))?
275 .try_into()?;
276 let storage_commitment = storage_commitment
277 .ok_or(proto::account::AccountHeader::missing_field(stringify!(storage_commitment)))?
278 .try_into()?;
279 let code_commitment = code_commitment
280 .ok_or(proto::account::AccountHeader::missing_field(stringify!(code_commitment)))?
281 .try_into()?;
282 let nonce = nonce.try_into().map_err(|_e| ConversionError::NotAValidFelt)?;
283
284 Ok(AccountHeader::new(
285 account_id,
286 nonce,
287 vault_root,
288 storage_commitment,
289 code_commitment,
290 ))
291 }
292}
293
294impl From<AccountHeader> for proto::account::AccountHeader {
295 fn from(header: AccountHeader) -> Self {
296 proto::account::AccountHeader {
297 account_id: Some(header.id().into()),
298 vault_root: Some(header.vault_root().into()),
299 storage_commitment: Some(header.storage_commitment().into()),
300 code_commitment: Some(header.code_commitment().into()),
301 nonce: header.nonce().as_int(),
302 }
303 }
304}
305
306impl From<AccountStorageHeader> for proto::account::AccountStorageHeader {
307 fn from(value: AccountStorageHeader) -> Self {
308 let slots = value
309 .slots()
310 .map(|slot_header| proto::account::account_storage_header::StorageSlot {
311 slot_name: slot_header.name().to_string(),
312 slot_type: storage_slot_type_to_raw(slot_header.slot_type()),
313 commitment: Some(proto::primitives::Digest::from(slot_header.value())),
314 })
315 .collect();
316
317 Self { slots }
318 }
319}
320
321#[derive(Debug, Clone, PartialEq, Eq)]
331pub enum AccountVaultDetails {
332 LimitExceeded,
335
336 Assets(Vec<Asset>),
338}
339
340impl AccountVaultDetails {
341 pub const MAX_RETURN_ENTRIES: usize = 1000;
344
345 pub fn new(vault: &AssetVault) -> Self {
346 if vault.assets().nth(Self::MAX_RETURN_ENTRIES).is_some() {
347 Self::LimitExceeded
348 } else {
349 Self::Assets(Vec::from_iter(vault.assets()))
350 }
351 }
352
353 pub fn empty() -> Self {
354 Self::Assets(Vec::new())
355 }
356
357 pub fn from_assets(assets: Vec<Asset>) -> Self {
359 if assets.len() > Self::MAX_RETURN_ENTRIES {
360 Self::LimitExceeded
361 } else {
362 Self::Assets(assets)
363 }
364 }
365}
366
367impl TryFrom<proto::rpc::AccountVaultDetails> for AccountVaultDetails {
368 type Error = ConversionError;
369
370 fn try_from(value: proto::rpc::AccountVaultDetails) -> Result<Self, Self::Error> {
371 let proto::rpc::AccountVaultDetails { too_many_assets, assets } = value;
372
373 if too_many_assets {
374 Ok(Self::LimitExceeded)
375 } else {
376 let parsed_assets =
377 Result::<Vec<_>, ConversionError>::from_iter(assets.into_iter().map(|asset| {
378 let asset = asset
379 .asset
380 .ok_or(proto::primitives::Asset::missing_field(stringify!(asset)))?;
381 let asset = Word::try_from(asset)?;
382 Asset::try_from(asset).map_err(ConversionError::AssetError)
383 }))?;
384 Ok(Self::Assets(parsed_assets))
385 }
386 }
387}
388
389impl From<AccountVaultDetails> for proto::rpc::AccountVaultDetails {
390 fn from(value: AccountVaultDetails) -> Self {
391 match value {
392 AccountVaultDetails::LimitExceeded => Self {
393 too_many_assets: true,
394 assets: Vec::new(),
395 },
396 AccountVaultDetails::Assets(assets) => Self {
397 too_many_assets: false,
398 assets: Vec::from_iter(assets.into_iter().map(|asset| proto::primitives::Asset {
399 asset: Some(proto::primitives::Digest::from(Word::from(asset))),
400 })),
401 },
402 }
403 }
404}
405
406#[derive(Debug, Clone, PartialEq, Eq)]
411pub struct AccountStorageMapDetails {
412 pub slot_name: StorageSlotName,
413 pub entries: StorageMapEntries,
414}
415
416#[derive(Debug, Clone, PartialEq, Eq)]
423pub enum StorageMapEntries {
424 LimitExceeded,
427
428 AllEntries(Vec<(StorageMapKey, Word)>),
431
432 EntriesWithProofs(Vec<SmtProof>),
435}
436
437impl AccountStorageMapDetails {
438 pub const MAX_RETURN_ENTRIES: usize = 1000;
440
441 pub const MAX_SMT_PROOF_ENTRIES: usize = QueryParamStorageMapKeyTotalLimit::LIMIT;
449
450 pub fn from_all_entries(slot_name: StorageSlotName, storage_map: &StorageMap) -> Self {
455 if storage_map.num_entries() > Self::MAX_RETURN_ENTRIES {
456 Self {
457 slot_name,
458 entries: StorageMapEntries::LimitExceeded,
459 }
460 } else {
461 let entries = Vec::from_iter(storage_map.entries().map(|(k, v)| (*k, *v)));
462 Self {
463 slot_name,
464 entries: StorageMapEntries::AllEntries(entries),
465 }
466 }
467 }
468
469 pub fn from_forest_entries(
473 slot_name: StorageSlotName,
474 entries: Vec<(StorageMapKey, Word)>,
475 ) -> Self {
476 if entries.len() > Self::MAX_RETURN_ENTRIES {
477 Self {
478 slot_name,
479 entries: StorageMapEntries::LimitExceeded,
480 }
481 } else {
482 Self {
483 slot_name,
484 entries: StorageMapEntries::AllEntries(entries),
485 }
486 }
487 }
488
489 pub fn from_proofs(slot_name: StorageSlotName, proofs: Vec<SmtProof>) -> Self {
494 if proofs.len() > Self::MAX_SMT_PROOF_ENTRIES {
495 Self {
496 slot_name,
497 entries: StorageMapEntries::LimitExceeded,
498 }
499 } else {
500 Self {
501 slot_name,
502 entries: StorageMapEntries::EntriesWithProofs(proofs),
503 }
504 }
505 }
506
507 pub fn limit_exceeded(slot_name: StorageSlotName) -> Self {
509 Self {
510 slot_name,
511 entries: StorageMapEntries::LimitExceeded,
512 }
513 }
514}
515
516impl TryFrom<proto::rpc::account_storage_details::AccountStorageMapDetails>
517 for AccountStorageMapDetails
518{
519 type Error = ConversionError;
520
521 fn try_from(
522 value: proto::rpc::account_storage_details::AccountStorageMapDetails,
523 ) -> Result<Self, Self::Error> {
524 use proto::rpc::account_storage_details::account_storage_map_details::{
525 all_map_entries::StorageMapEntry,
526 map_entries_with_proofs::StorageMapEntryWithProof,
527 AllMapEntries,
528 Entries as ProtoEntries,
529 MapEntriesWithProofs,
530 };
531
532 let proto::rpc::account_storage_details::AccountStorageMapDetails {
533 slot_name,
534 too_many_entries,
535 entries,
536 } = value;
537
538 let slot_name = StorageSlotName::new(slot_name)?;
539
540 let entries = if too_many_entries {
541 StorageMapEntries::LimitExceeded
542 } else {
543 match entries {
544 None => {
545 return Err(
546 proto::rpc::account_storage_details::AccountStorageMapDetails::missing_field(
547 stringify!(entries),
548 ),
549 );
550 },
551 Some(ProtoEntries::AllEntries(AllMapEntries { entries })) => {
552 let entries = entries
553 .into_iter()
554 .map(|entry| {
555 let key = entry
556 .key
557 .ok_or(StorageMapEntry::missing_field(stringify!(key)))?
558 .try_into()
559 .map(StorageMapKey::new)?;
560 let value = entry
561 .value
562 .ok_or(StorageMapEntry::missing_field(stringify!(value)))?
563 .try_into()?;
564 Ok((key, value))
565 })
566 .collect::<Result<Vec<_>, ConversionError>>()?;
567 StorageMapEntries::AllEntries(entries)
568 },
569 Some(ProtoEntries::EntriesWithProofs(MapEntriesWithProofs { entries })) => {
570 let proofs = entries
571 .into_iter()
572 .map(|entry| {
573 let smt_opening = entry.proof.ok_or(
574 StorageMapEntryWithProof::missing_field(stringify!(proof)),
575 )?;
576 SmtProof::try_from(smt_opening)
577 })
578 .collect::<Result<Vec<_>, ConversionError>>()?;
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 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 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 proto::rpc::AccountStorageDetails { header, map_details } = value;
675
676 let header = header
677 .ok_or(proto::rpc::AccountStorageDetails::missing_field(stringify!(header)))?
678 .try_into()?;
679
680 let map_details = try_convert(map_details).collect::<Result<Vec<_>, _>>()?;
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
697const fn 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 _ => return Err(ConversionError::EnumDiscriminantOutOfRange),
702 })
703}
704
705const fn storage_slot_type_to_raw(slot_type: StorageSlotType) -> u32 {
706 match slot_type {
707 StorageSlotType::Value => 0,
708 StorageSlotType::Map => 1,
709 }
710}
711
712pub struct AccountResponse {
717 pub block_num: BlockNumber,
718 pub witness: AccountWitness,
719 pub details: Option<AccountDetails>,
720}
721
722impl TryFrom<proto::rpc::AccountResponse> for AccountResponse {
723 type Error = ConversionError;
724
725 fn try_from(value: proto::rpc::AccountResponse) -> Result<Self, Self::Error> {
726 let proto::rpc::AccountResponse { block_num, witness, details } = value;
727
728 let block_num = block_num
729 .ok_or(proto::rpc::AccountResponse::missing_field(stringify!(block_num)))?
730 .into();
731
732 let witness = witness
733 .ok_or(proto::rpc::AccountResponse::missing_field(stringify!(witness)))?
734 .try_into()?;
735
736 let details = details.map(TryFrom::try_from).transpose()?;
737
738 Ok(AccountResponse { block_num, witness, details })
739 }
740}
741
742impl From<AccountResponse> for proto::rpc::AccountResponse {
743 fn from(value: AccountResponse) -> Self {
744 let AccountResponse { block_num, witness, details } = value;
745
746 Self {
747 witness: Some(witness.into()),
748 details: details.map(Into::into),
749 block_num: Some(block_num.into()),
750 }
751 }
752}
753
754pub struct AccountDetails {
759 pub account_header: AccountHeader,
760 pub account_code: Option<Vec<u8>>,
761 pub vault_details: AccountVaultDetails,
762 pub storage_details: AccountStorageDetails,
763}
764
765impl AccountDetails {
766 pub fn with_storage_limits_exceeded(
768 account_header: AccountHeader,
769 account_code: Option<Vec<u8>>,
770 vault_details: AccountVaultDetails,
771 storage_header: AccountStorageHeader,
772 slot_names: impl IntoIterator<Item = StorageSlotName>,
773 ) -> Self {
774 Self {
775 account_header,
776 account_code,
777 vault_details,
778 storage_details: AccountStorageDetails::all_limits_exceeded(storage_header, slot_names),
779 }
780 }
781}
782
783impl TryFrom<proto::rpc::account_response::AccountDetails> for AccountDetails {
784 type Error = ConversionError;
785
786 fn try_from(value: proto::rpc::account_response::AccountDetails) -> Result<Self, Self::Error> {
787 let proto::rpc::account_response::AccountDetails {
788 header,
789 code,
790 vault_details,
791 storage_details,
792 } = value;
793
794 let account_header = header
795 .ok_or(proto::rpc::account_response::AccountDetails::missing_field(stringify!(header)))?
796 .try_into()?;
797
798 let storage_details = storage_details
799 .ok_or(proto::rpc::account_response::AccountDetails::missing_field(stringify!(
800 storage_details
801 )))?
802 .try_into()?;
803
804 let vault_details = vault_details
805 .ok_or(proto::rpc::account_response::AccountDetails::missing_field(stringify!(
806 vault_details
807 )))?
808 .try_into()?;
809 let account_code = code;
810
811 Ok(AccountDetails {
812 account_header,
813 account_code,
814 vault_details,
815 storage_details,
816 })
817 }
818}
819
820impl From<AccountDetails> for proto::rpc::account_response::AccountDetails {
821 fn from(value: AccountDetails) -> Self {
822 let AccountDetails {
823 account_header,
824 storage_details,
825 account_code,
826 vault_details,
827 } = value;
828
829 let header = Some(proto::account::AccountHeader::from(account_header));
830 let storage_details = Some(storage_details.into());
831 let code = account_code;
832 let vault_details = Some(vault_details.into());
833
834 Self {
835 header,
836 storage_details,
837 code,
838 vault_details,
839 }
840 }
841}
842
843impl TryFrom<proto::account::AccountWitness> for AccountWitness {
847 type Error = ConversionError;
848
849 fn try_from(account_witness: proto::account::AccountWitness) -> Result<Self, Self::Error> {
850 let witness_id = account_witness
851 .witness_id
852 .ok_or(proto::account::AccountWitness::missing_field(stringify!(witness_id)))?
853 .try_into()?;
854 let commitment = account_witness
855 .commitment
856 .ok_or(proto::account::AccountWitness::missing_field(stringify!(commitment)))?
857 .try_into()?;
858 let path = account_witness
859 .path
860 .ok_or(proto::account::AccountWitness::missing_field(stringify!(path)))?
861 .try_into()?;
862
863 AccountWitness::new(witness_id, commitment, path).map_err(|err| {
864 ConversionError::deserialization_error(
865 "AccountWitness",
866 DeserializationError::InvalidValue(err.to_string()),
867 )
868 })
869 }
870}
871
872impl From<AccountWitness> for proto::account::AccountWitness {
873 fn from(witness: AccountWitness) -> Self {
874 Self {
875 account_id: Some(witness.id().into()),
876 witness_id: Some(witness.id().into()),
877 commitment: Some(witness.state_commitment().into()),
878 path: Some(witness.into_proof().into_parts().0.into()),
879 }
880 }
881}
882
883#[derive(Clone, Debug, PartialEq, Eq)]
887pub struct AccountWitnessRecord {
888 pub account_id: AccountId,
889 pub witness: AccountWitness,
890}
891
892impl TryFrom<proto::account::AccountWitness> for AccountWitnessRecord {
893 type Error = ConversionError;
894
895 fn try_from(
896 account_witness_record: proto::account::AccountWitness,
897 ) -> Result<Self, Self::Error> {
898 let witness_id = account_witness_record
899 .witness_id
900 .ok_or(proto::account::AccountWitness::missing_field(stringify!(witness_id)))?
901 .try_into()?;
902 let commitment = account_witness_record
903 .commitment
904 .ok_or(proto::account::AccountWitness::missing_field(stringify!(commitment)))?
905 .try_into()?;
906 let path: SparseMerklePath = account_witness_record
907 .path
908 .as_ref()
909 .ok_or(proto::account::AccountWitness::missing_field(stringify!(path)))?
910 .clone()
911 .try_into()?;
912
913 let witness = AccountWitness::new(witness_id, commitment, path).map_err(|err| {
914 ConversionError::deserialization_error(
915 "AccountWitness",
916 DeserializationError::InvalidValue(err.to_string()),
917 )
918 })?;
919
920 Ok(Self {
921 account_id: account_witness_record
922 .account_id
923 .ok_or(proto::account::AccountWitness::missing_field(stringify!(account_id)))?
924 .try_into()?,
925 witness,
926 })
927 }
928}
929
930impl From<AccountWitnessRecord> for proto::account::AccountWitness {
931 fn from(from: AccountWitnessRecord) -> Self {
932 Self {
933 account_id: Some(from.account_id.into()),
934 witness_id: Some(from.witness.id().into()),
935 commitment: Some(from.witness.state_commitment().into()),
936 path: Some(from.witness.path().clone().into()),
937 }
938 }
939}
940
941#[derive(Debug)]
946pub struct AccountState {
947 pub account_id: AccountId,
949 pub account_commitment: Option<Word>,
951}
952
953impl Display for AccountState {
954 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
955 f.write_fmt(format_args!(
956 "{{ account_id: {}, account_commitment: {} }}",
957 self.account_id,
958 format_opt(self.account_commitment.as_ref()),
959 ))
960 }
961}
962
963impl TryFrom<proto::store::transaction_inputs::AccountTransactionInputRecord> for AccountState {
964 type Error = ConversionError;
965
966 fn try_from(
967 from: proto::store::transaction_inputs::AccountTransactionInputRecord,
968 ) -> Result<Self, Self::Error> {
969 let account_id = from
970 .account_id
971 .ok_or(proto::store::transaction_inputs::AccountTransactionInputRecord::missing_field(
972 stringify!(account_id),
973 ))?
974 .try_into()?;
975
976 let account_commitment = from
977 .account_commitment
978 .ok_or(proto::store::transaction_inputs::AccountTransactionInputRecord::missing_field(
979 stringify!(account_commitment),
980 ))?
981 .try_into()?;
982
983 let account_commitment = if account_commitment == Word::empty() {
986 None
987 } else {
988 Some(account_commitment)
989 };
990
991 Ok(Self { account_id, account_commitment })
992 }
993}
994
995impl From<AccountState> for proto::store::transaction_inputs::AccountTransactionInputRecord {
996 fn from(from: AccountState) -> Self {
997 Self {
998 account_id: Some(from.account_id.into()),
999 account_commitment: from.account_commitment.map(Into::into),
1000 }
1001 }
1002}
1003
1004impl TryFrom<proto::primitives::Asset> for Asset {
1008 type Error = ConversionError;
1009
1010 fn try_from(value: proto::primitives::Asset) -> Result<Self, Self::Error> {
1011 let inner = value.asset.ok_or(proto::primitives::Asset::missing_field("asset"))?;
1012 let word = Word::try_from(inner)?;
1013
1014 Asset::try_from(word).map_err(ConversionError::AssetError)
1015 }
1016}
1017
1018impl From<Asset> for proto::primitives::Asset {
1019 fn from(asset_from: Asset) -> Self {
1020 proto::primitives::Asset {
1021 asset: Some(Word::from(asset_from).into()),
1022 }
1023 }
1024}
1025
1026pub type AccountPrefix = u32;
1030
1031#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
1037pub struct NetworkAccountId(AccountId);
1038
1039impl std::fmt::Display for NetworkAccountId {
1040 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1041 std::fmt::Display::fmt(&self.0, f)
1042 }
1043}
1044
1045impl NetworkAccountId {
1046 pub fn inner(&self) -> AccountId {
1048 self.0
1049 }
1050
1051 pub fn prefix(&self) -> AccountPrefix {
1053 get_account_id_tag_prefix(self.0)
1054 }
1055}
1056
1057impl TryFrom<AccountId> for NetworkAccountId {
1058 type Error = NetworkAccountError;
1059
1060 fn try_from(id: AccountId) -> Result<Self, Self::Error> {
1061 if !id.is_network() {
1062 return Err(NetworkAccountError::NotNetworkAccount(id));
1063 }
1064 Ok(NetworkAccountId(id))
1065 }
1066}
1067
1068impl TryFrom<&NoteAttachment> for NetworkAccountId {
1069 type Error = NetworkAccountError;
1070
1071 fn try_from(attachment: &NoteAttachment) -> Result<Self, Self::Error> {
1072 let target = NetworkAccountTarget::try_from(attachment)
1073 .map_err(NetworkAccountError::InvalidAttachment)?;
1074 Ok(NetworkAccountId(target.target_id()))
1075 }
1076}
1077
1078impl TryFrom<NoteAttachment> for NetworkAccountId {
1079 type Error = NetworkAccountError;
1080
1081 fn try_from(attachment: NoteAttachment) -> Result<Self, Self::Error> {
1082 NetworkAccountId::try_from(&attachment)
1083 }
1084}
1085
1086impl From<NetworkAccountId> for AccountId {
1087 fn from(value: NetworkAccountId) -> Self {
1088 value.inner()
1089 }
1090}
1091
1092impl From<NetworkAccountId> for u32 {
1093 fn from(value: NetworkAccountId) -> Self {
1096 value.prefix()
1097 }
1098}
1099
1100#[derive(Debug, Error)]
1101pub enum NetworkAccountError {
1102 #[error("account ID {0} is not a valid network account ID")]
1103 NotNetworkAccount(AccountId),
1104 #[error("invalid network account attachment: {0}")]
1105 InvalidAttachment(#[source] NetworkAccountTargetError),
1106}
1107
1108fn get_account_id_tag_prefix(id: AccountId) -> AccountPrefix {
1110 (id.prefix().as_u64() >> 34) as AccountPrefix
1111}