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 StorageSlotHeader,
13 StorageSlotName,
14 StorageSlotType,
15};
16use miden_protocol::asset::{Asset, AssetVault};
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::note::NoteAttachment;
22use miden_protocol::utils::{Deserializable, DeserializationError, Serializable};
23use miden_standards::note::{NetworkAccountTarget, NetworkAccountTargetError};
24use thiserror::Error;
25
26use super::try_convert;
27use crate::errors::{ConversionError, MissingFieldHelper};
28use crate::generated::{self as proto};
29
30#[cfg(test)]
31mod tests;
32
33impl Display for proto::account::AccountId {
37 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38 write!(f, "0x")?;
39 for byte in &self.id {
40 write!(f, "{byte:02x}")?;
41 }
42 Ok(())
43 }
44}
45
46impl Debug for proto::account::AccountId {
47 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48 Display::fmt(self, f)
49 }
50}
51
52impl TryFrom<proto::account::AccountId> for AccountId {
56 type Error = ConversionError;
57
58 fn try_from(account_id: proto::account::AccountId) -> Result<Self, Self::Error> {
59 AccountId::read_from_bytes(&account_id.id).map_err(|_| ConversionError::NotAValidFelt)
60 }
61}
62
63impl 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#[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
113impl 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 slot_name = StorageSlotName::new(slot.slot_name)?;
126 let slot_type = storage_slot_type_from_raw(slot.slot_type)?;
127 let commitment =
128 slot.commitment.ok_or(ConversionError::NotAValidFelt)?.try_into()?;
129 Ok(StorageSlotHeader::new(slot_name, slot_type, commitment))
130 })
131 .collect::<Result<Vec<_>, ConversionError>>()?;
132
133 Ok(AccountStorageHeader::new(slot_headers)?)
134 }
135}
136
137pub struct AccountRequest {
142 pub account_id: AccountId,
143 pub block_num: Option<BlockNumber>,
145 pub details: Option<AccountDetailRequest>,
146}
147
148impl TryFrom<proto::rpc::AccountRequest> for AccountRequest {
149 type Error = ConversionError;
150
151 fn try_from(value: proto::rpc::AccountRequest) -> Result<Self, Self::Error> {
152 let proto::rpc::AccountRequest { account_id, block_num, details } = value;
153
154 let account_id = account_id
155 .ok_or(proto::rpc::AccountRequest::missing_field(stringify!(account_id)))?
156 .try_into()?;
157 let block_num = block_num.map(Into::into);
158
159 let details = details.map(TryFrom::try_from).transpose()?;
160
161 Ok(AccountRequest { account_id, block_num, details })
162 }
163}
164
165pub struct AccountDetailRequest {
167 pub code_commitment: Option<Word>,
168 pub asset_vault_commitment: Option<Word>,
169 pub storage_requests: Vec<StorageMapRequest>,
170}
171
172impl TryFrom<proto::rpc::account_request::AccountDetailRequest> for AccountDetailRequest {
173 type Error = ConversionError;
174
175 fn try_from(
176 value: proto::rpc::account_request::AccountDetailRequest,
177 ) -> Result<Self, Self::Error> {
178 let proto::rpc::account_request::AccountDetailRequest {
179 code_commitment,
180 asset_vault_commitment,
181 storage_maps,
182 } = value;
183
184 let code_commitment = code_commitment.map(TryFrom::try_from).transpose()?;
185 let asset_vault_commitment = asset_vault_commitment.map(TryFrom::try_from).transpose()?;
186 let storage_requests = try_convert(storage_maps).collect::<Result<_, _>>()?;
187
188 Ok(AccountDetailRequest {
189 code_commitment,
190 asset_vault_commitment,
191 storage_requests,
192 })
193 }
194}
195
196#[derive(Debug, Clone, PartialEq, Eq)]
197pub struct StorageMapRequest {
198 pub slot_name: StorageSlotName,
199 pub slot_data: SlotData,
200}
201
202impl TryFrom<proto::rpc::account_request::account_detail_request::StorageMapDetailRequest>
203 for StorageMapRequest
204{
205 type Error = ConversionError;
206
207 fn try_from(
208 value: proto::rpc::account_request::account_detail_request::StorageMapDetailRequest,
209 ) -> Result<Self, Self::Error> {
210 let proto::rpc::account_request::account_detail_request::StorageMapDetailRequest {
211 slot_name,
212 slot_data,
213 } = value;
214
215 let slot_name = StorageSlotName::new(slot_name)?;
216 let slot_data = slot_data.ok_or(proto::rpc::account_request::account_detail_request::StorageMapDetailRequest::missing_field(stringify!(slot_data)))?.try_into()?;
217
218 Ok(StorageMapRequest { slot_name, slot_data })
219 }
220}
221
222#[derive(Debug, Clone, PartialEq, Eq)]
224pub enum SlotData {
225 All,
226 MapKeys(Vec<Word>),
227}
228
229impl
230 TryFrom<
231 proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData,
232 > for SlotData
233{
234 type Error = ConversionError;
235
236 fn try_from(
237 value: proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData,
238 ) -> Result<Self, Self::Error> {
239 use proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData as ProtoSlotData;
240
241 Ok(match value {
242 ProtoSlotData::AllEntries(true) => SlotData::All,
243 ProtoSlotData::AllEntries(false) => {
244 return Err(ConversionError::EnumDiscriminantOutOfRange);
245 },
246 ProtoSlotData::MapKeys(keys) => {
247 let keys = try_convert(keys.map_keys).collect::<Result<Vec<_>, _>>()?;
248 SlotData::MapKeys(keys)
249 },
250 })
251 }
252}
253
254impl TryFrom<proto::account::AccountHeader> for AccountHeader {
258 type Error = ConversionError;
259
260 fn try_from(value: proto::account::AccountHeader) -> Result<Self, Self::Error> {
261 let proto::account::AccountHeader {
262 account_id,
263 vault_root,
264 storage_commitment,
265 code_commitment,
266 nonce,
267 } = value;
268
269 let account_id = account_id
270 .ok_or(proto::account::AccountHeader::missing_field(stringify!(account_id)))?
271 .try_into()?;
272 let vault_root = vault_root
273 .ok_or(proto::account::AccountHeader::missing_field(stringify!(vault_root)))?
274 .try_into()?;
275 let storage_commitment = storage_commitment
276 .ok_or(proto::account::AccountHeader::missing_field(stringify!(storage_commitment)))?
277 .try_into()?;
278 let code_commitment = code_commitment
279 .ok_or(proto::account::AccountHeader::missing_field(stringify!(code_commitment)))?
280 .try_into()?;
281 let nonce = nonce.try_into().map_err(|_e| ConversionError::NotAValidFelt)?;
282
283 Ok(AccountHeader::new(
284 account_id,
285 nonce,
286 vault_root,
287 storage_commitment,
288 code_commitment,
289 ))
290 }
291}
292
293impl From<AccountHeader> for proto::account::AccountHeader {
294 fn from(header: AccountHeader) -> Self {
295 proto::account::AccountHeader {
296 account_id: Some(header.id().into()),
297 vault_root: Some(header.vault_root().into()),
298 storage_commitment: Some(header.storage_commitment().into()),
299 code_commitment: Some(header.code_commitment().into()),
300 nonce: header.nonce().as_int(),
301 }
302 }
303}
304
305impl From<AccountStorageHeader> for proto::account::AccountStorageHeader {
306 fn from(value: AccountStorageHeader) -> Self {
307 let slots = value
308 .slots()
309 .map(|slot_header| proto::account::account_storage_header::StorageSlot {
310 slot_name: slot_header.name().to_string(),
311 slot_type: storage_slot_type_to_raw(slot_header.slot_type()),
312 commitment: Some(proto::primitives::Digest::from(slot_header.value())),
313 })
314 .collect();
315
316 Self { slots }
317 }
318}
319
320#[derive(Debug, Clone, PartialEq, Eq)]
330pub enum AccountVaultDetails {
331 LimitExceeded,
334
335 Assets(Vec<Asset>),
337}
338
339impl AccountVaultDetails {
340 pub const MAX_RETURN_ENTRIES: usize = 1000;
343
344 pub fn new(vault: &AssetVault) -> Self {
345 if vault.assets().nth(Self::MAX_RETURN_ENTRIES).is_some() {
346 Self::LimitExceeded
347 } else {
348 Self::Assets(Vec::from_iter(vault.assets()))
349 }
350 }
351
352 pub fn empty() -> Self {
353 Self::Assets(Vec::new())
354 }
355
356 pub fn from_assets(assets: Vec<Asset>) -> Self {
358 if assets.len() > Self::MAX_RETURN_ENTRIES {
359 Self::LimitExceeded
360 } else {
361 Self::Assets(assets)
362 }
363 }
364}
365
366impl TryFrom<proto::rpc::AccountVaultDetails> for AccountVaultDetails {
367 type Error = ConversionError;
368
369 fn try_from(value: proto::rpc::AccountVaultDetails) -> Result<Self, Self::Error> {
370 let proto::rpc::AccountVaultDetails { too_many_assets, assets } = value;
371
372 if too_many_assets {
373 Ok(Self::LimitExceeded)
374 } else {
375 let parsed_assets =
376 Result::<Vec<_>, ConversionError>::from_iter(assets.into_iter().map(|asset| {
377 let asset = asset
378 .asset
379 .ok_or(proto::primitives::Asset::missing_field(stringify!(asset)))?;
380 let asset = Word::try_from(asset)?;
381 Asset::try_from(asset).map_err(ConversionError::AssetError)
382 }))?;
383 Ok(Self::Assets(parsed_assets))
384 }
385 }
386}
387
388impl From<AccountVaultDetails> for proto::rpc::AccountVaultDetails {
389 fn from(value: AccountVaultDetails) -> Self {
390 match value {
391 AccountVaultDetails::LimitExceeded => Self {
392 too_many_assets: true,
393 assets: Vec::new(),
394 },
395 AccountVaultDetails::Assets(assets) => Self {
396 too_many_assets: false,
397 assets: Vec::from_iter(assets.into_iter().map(|asset| proto::primitives::Asset {
398 asset: Some(proto::primitives::Digest::from(Word::from(asset))),
399 })),
400 },
401 }
402 }
403}
404
405#[derive(Debug, Clone, PartialEq, Eq)]
410pub struct AccountStorageMapDetails {
411 pub slot_name: StorageSlotName,
412 pub entries: StorageMapEntries,
413}
414
415#[derive(Debug, Clone, PartialEq, Eq)]
422pub enum StorageMapEntries {
423 LimitExceeded,
426
427 AllEntries(Vec<(Word, Word)>),
430
431 EntriesWithProofs(Vec<SmtProof>),
434}
435
436impl AccountStorageMapDetails {
437 pub const MAX_RETURN_ENTRIES: usize = 1000;
439
440 pub const MAX_SMT_PROOF_ENTRIES: usize = QueryParamStorageMapKeyTotalLimit::LIMIT;
448
449 pub fn from_all_entries(slot_name: StorageSlotName, storage_map: &StorageMap) -> Self {
454 if storage_map.num_entries() > Self::MAX_RETURN_ENTRIES {
455 Self {
456 slot_name,
457 entries: StorageMapEntries::LimitExceeded,
458 }
459 } else {
460 let entries = Vec::from_iter(storage_map.entries().map(|(k, v)| (*k, *v)));
461 Self {
462 slot_name,
463 entries: StorageMapEntries::AllEntries(entries),
464 }
465 }
466 }
467
468 pub fn from_forest_entries(slot_name: StorageSlotName, entries: Vec<(Word, Word)>) -> Self {
472 if entries.len() > Self::MAX_RETURN_ENTRIES {
473 Self {
474 slot_name,
475 entries: StorageMapEntries::LimitExceeded,
476 }
477 } else {
478 Self {
479 slot_name,
480 entries: StorageMapEntries::AllEntries(entries),
481 }
482 }
483 }
484
485 pub fn from_proofs(slot_name: StorageSlotName, proofs: Vec<SmtProof>) -> Self {
490 if proofs.len() > Self::MAX_SMT_PROOF_ENTRIES {
491 Self {
492 slot_name,
493 entries: StorageMapEntries::LimitExceeded,
494 }
495 } else {
496 Self {
497 slot_name,
498 entries: StorageMapEntries::EntriesWithProofs(proofs),
499 }
500 }
501 }
502
503 pub fn limit_exceeded(slot_name: StorageSlotName) -> Self {
505 Self {
506 slot_name,
507 entries: StorageMapEntries::LimitExceeded,
508 }
509 }
510}
511
512impl TryFrom<proto::rpc::account_storage_details::AccountStorageMapDetails>
513 for AccountStorageMapDetails
514{
515 type Error = ConversionError;
516
517 fn try_from(
518 value: proto::rpc::account_storage_details::AccountStorageMapDetails,
519 ) -> Result<Self, Self::Error> {
520 use proto::rpc::account_storage_details::account_storage_map_details::{
521 all_map_entries::StorageMapEntry,
522 map_entries_with_proofs::StorageMapEntryWithProof,
523 AllMapEntries,
524 Entries as ProtoEntries,
525 MapEntriesWithProofs,
526 };
527
528 let proto::rpc::account_storage_details::AccountStorageMapDetails {
529 slot_name,
530 too_many_entries,
531 entries,
532 } = value;
533
534 let slot_name = StorageSlotName::new(slot_name)?;
535
536 let entries = if too_many_entries {
537 StorageMapEntries::LimitExceeded
538 } else {
539 match entries {
540 None => {
541 return Err(
542 proto::rpc::account_storage_details::AccountStorageMapDetails::missing_field(
543 stringify!(entries),
544 ),
545 );
546 },
547 Some(ProtoEntries::AllEntries(AllMapEntries { entries })) => {
548 let entries = entries
549 .into_iter()
550 .map(|entry| {
551 let key = entry
552 .key
553 .ok_or(StorageMapEntry::missing_field(stringify!(key)))?
554 .try_into()?;
555 let value = entry
556 .value
557 .ok_or(StorageMapEntry::missing_field(stringify!(value)))?
558 .try_into()?;
559 Ok((key, value))
560 })
561 .collect::<Result<Vec<_>, ConversionError>>()?;
562 StorageMapEntries::AllEntries(entries)
563 },
564 Some(ProtoEntries::EntriesWithProofs(MapEntriesWithProofs { entries })) => {
565 let proofs = entries
566 .into_iter()
567 .map(|entry| {
568 let smt_opening = entry.proof.ok_or(
569 StorageMapEntryWithProof::missing_field(stringify!(proof)),
570 )?;
571 SmtProof::try_from(smt_opening)
572 })
573 .collect::<Result<Vec<_>, ConversionError>>()?;
574 StorageMapEntries::EntriesWithProofs(proofs)
575 },
576 }
577 };
578
579 Ok(Self { slot_name, entries })
580 }
581}
582
583impl From<AccountStorageMapDetails>
584 for proto::rpc::account_storage_details::AccountStorageMapDetails
585{
586 fn from(value: AccountStorageMapDetails) -> Self {
587 use proto::rpc::account_storage_details::account_storage_map_details::{
588 AllMapEntries,
589 Entries as ProtoEntries,
590 MapEntriesWithProofs,
591 };
592
593 let AccountStorageMapDetails { slot_name, entries } = value;
594
595 let (too_many_entries, proto_entries) = match entries {
596 StorageMapEntries::LimitExceeded => (true, None),
597 StorageMapEntries::AllEntries(entries) => {
598 let all = AllMapEntries {
599 entries: Vec::from_iter(entries.into_iter().map(|(key, value)| {
600 proto::rpc::account_storage_details::account_storage_map_details::all_map_entries::StorageMapEntry {
601 key: Some(key.into()),
602 value: Some(value.into()),
603 }
604 })),
605 };
606 (false, Some(ProtoEntries::AllEntries(all)))
607 },
608 StorageMapEntries::EntriesWithProofs(proofs) => {
609 use miden_protocol::crypto::merkle::smt::SmtLeaf;
610
611 let with_proofs = MapEntriesWithProofs {
612 entries: Vec::from_iter(proofs.into_iter().map(|proof| {
613 let (key, value) = match proof.leaf() {
615 SmtLeaf::Empty(_) => {
616 (miden_protocol::EMPTY_WORD, miden_protocol::EMPTY_WORD)
617 },
618 SmtLeaf::Single((k, v)) => (*k, *v),
619 SmtLeaf::Multiple(entries) => entries.iter().next().map_or(
620 (miden_protocol::EMPTY_WORD, miden_protocol::EMPTY_WORD),
621 |(k, v)| (*k, *v),
622 ),
623 };
624 let smt_opening = proto::primitives::SmtOpening::from(proof);
625 proto::rpc::account_storage_details::account_storage_map_details::map_entries_with_proofs::StorageMapEntryWithProof {
626 key: Some(key.into()),
627 value: Some(value.into()),
628 proof: Some(smt_opening),
629 }
630 })),
631 };
632 (false, Some(ProtoEntries::EntriesWithProofs(with_proofs)))
633 },
634 };
635
636 Self {
637 slot_name: slot_name.to_string(),
638 too_many_entries,
639 entries: proto_entries,
640 }
641 }
642}
643
644#[derive(Debug, Clone, PartialEq)]
645pub struct AccountStorageDetails {
646 pub header: AccountStorageHeader,
647 pub map_details: Vec<AccountStorageMapDetails>,
648}
649
650impl AccountStorageDetails {
651 pub fn all_limits_exceeded(
653 header: AccountStorageHeader,
654 slot_names: impl IntoIterator<Item = StorageSlotName>,
655 ) -> Self {
656 Self {
657 header,
658 map_details: Vec::from_iter(
659 slot_names.into_iter().map(AccountStorageMapDetails::limit_exceeded),
660 ),
661 }
662 }
663}
664
665impl TryFrom<proto::rpc::AccountStorageDetails> for AccountStorageDetails {
666 type Error = ConversionError;
667
668 fn try_from(value: proto::rpc::AccountStorageDetails) -> Result<Self, Self::Error> {
669 let proto::rpc::AccountStorageDetails { header, map_details } = value;
670
671 let header = header
672 .ok_or(proto::rpc::AccountStorageDetails::missing_field(stringify!(header)))?
673 .try_into()?;
674
675 let map_details = try_convert(map_details).collect::<Result<Vec<_>, _>>()?;
676
677 Ok(Self { header, map_details })
678 }
679}
680
681impl From<AccountStorageDetails> for proto::rpc::AccountStorageDetails {
682 fn from(value: AccountStorageDetails) -> Self {
683 let AccountStorageDetails { header, map_details } = value;
684
685 Self {
686 header: Some(header.into()),
687 map_details: map_details.into_iter().map(Into::into).collect(),
688 }
689 }
690}
691
692const fn storage_slot_type_from_raw(slot_type: u32) -> Result<StorageSlotType, ConversionError> {
693 Ok(match slot_type {
694 0 => StorageSlotType::Value,
695 1 => StorageSlotType::Map,
696 _ => return Err(ConversionError::EnumDiscriminantOutOfRange),
697 })
698}
699
700const fn storage_slot_type_to_raw(slot_type: StorageSlotType) -> u32 {
701 match slot_type {
702 StorageSlotType::Value => 0,
703 StorageSlotType::Map => 1,
704 }
705}
706
707pub struct AccountResponse {
712 pub block_num: BlockNumber,
713 pub witness: AccountWitness,
714 pub details: Option<AccountDetails>,
715}
716
717impl TryFrom<proto::rpc::AccountResponse> for AccountResponse {
718 type Error = ConversionError;
719
720 fn try_from(value: proto::rpc::AccountResponse) -> Result<Self, Self::Error> {
721 let proto::rpc::AccountResponse { block_num, witness, details } = value;
722
723 let block_num = block_num
724 .ok_or(proto::rpc::AccountResponse::missing_field(stringify!(block_num)))?
725 .into();
726
727 let witness = witness
728 .ok_or(proto::rpc::AccountResponse::missing_field(stringify!(witness)))?
729 .try_into()?;
730
731 let details = details.map(TryFrom::try_from).transpose()?;
732
733 Ok(AccountResponse { block_num, witness, details })
734 }
735}
736
737impl From<AccountResponse> for proto::rpc::AccountResponse {
738 fn from(value: AccountResponse) -> Self {
739 let AccountResponse { block_num, witness, details } = value;
740
741 Self {
742 witness: Some(witness.into()),
743 details: details.map(Into::into),
744 block_num: Some(block_num.into()),
745 }
746 }
747}
748
749pub struct AccountDetails {
754 pub account_header: AccountHeader,
755 pub account_code: Option<Vec<u8>>,
756 pub vault_details: AccountVaultDetails,
757 pub storage_details: AccountStorageDetails,
758}
759
760impl AccountDetails {
761 pub fn with_storage_limits_exceeded(
763 account_header: AccountHeader,
764 account_code: Option<Vec<u8>>,
765 vault_details: AccountVaultDetails,
766 storage_header: AccountStorageHeader,
767 slot_names: impl IntoIterator<Item = StorageSlotName>,
768 ) -> Self {
769 Self {
770 account_header,
771 account_code,
772 vault_details,
773 storage_details: AccountStorageDetails::all_limits_exceeded(storage_header, slot_names),
774 }
775 }
776}
777
778impl TryFrom<proto::rpc::account_response::AccountDetails> for AccountDetails {
779 type Error = ConversionError;
780
781 fn try_from(value: proto::rpc::account_response::AccountDetails) -> Result<Self, Self::Error> {
782 let proto::rpc::account_response::AccountDetails {
783 header,
784 code,
785 vault_details,
786 storage_details,
787 } = value;
788
789 let account_header = header
790 .ok_or(proto::rpc::account_response::AccountDetails::missing_field(stringify!(header)))?
791 .try_into()?;
792
793 let storage_details = storage_details
794 .ok_or(proto::rpc::account_response::AccountDetails::missing_field(stringify!(
795 storage_details
796 )))?
797 .try_into()?;
798
799 let vault_details = vault_details
800 .ok_or(proto::rpc::account_response::AccountDetails::missing_field(stringify!(
801 vault_details
802 )))?
803 .try_into()?;
804 let account_code = code;
805
806 Ok(AccountDetails {
807 account_header,
808 account_code,
809 vault_details,
810 storage_details,
811 })
812 }
813}
814
815impl From<AccountDetails> for proto::rpc::account_response::AccountDetails {
816 fn from(value: AccountDetails) -> Self {
817 let AccountDetails {
818 account_header,
819 storage_details,
820 account_code,
821 vault_details,
822 } = value;
823
824 let header = Some(proto::account::AccountHeader::from(account_header));
825 let storage_details = Some(storage_details.into());
826 let code = account_code;
827 let vault_details = Some(vault_details.into());
828
829 Self {
830 header,
831 storage_details,
832 code,
833 vault_details,
834 }
835 }
836}
837
838impl TryFrom<proto::account::AccountWitness> for AccountWitness {
842 type Error = ConversionError;
843
844 fn try_from(account_witness: proto::account::AccountWitness) -> Result<Self, Self::Error> {
845 let witness_id = account_witness
846 .witness_id
847 .ok_or(proto::account::AccountWitness::missing_field(stringify!(witness_id)))?
848 .try_into()?;
849 let commitment = account_witness
850 .commitment
851 .ok_or(proto::account::AccountWitness::missing_field(stringify!(commitment)))?
852 .try_into()?;
853 let path = account_witness
854 .path
855 .ok_or(proto::account::AccountWitness::missing_field(stringify!(path)))?
856 .try_into()?;
857
858 AccountWitness::new(witness_id, commitment, path).map_err(|err| {
859 ConversionError::deserialization_error(
860 "AccountWitness",
861 DeserializationError::InvalidValue(err.to_string()),
862 )
863 })
864 }
865}
866
867impl From<AccountWitness> for proto::account::AccountWitness {
868 fn from(witness: AccountWitness) -> Self {
869 Self {
870 account_id: Some(witness.id().into()),
871 witness_id: Some(witness.id().into()),
872 commitment: Some(witness.state_commitment().into()),
873 path: Some(witness.into_proof().into_parts().0.into()),
874 }
875 }
876}
877
878#[derive(Clone, Debug, PartialEq, Eq)]
882pub struct AccountWitnessRecord {
883 pub account_id: AccountId,
884 pub witness: AccountWitness,
885}
886
887impl TryFrom<proto::account::AccountWitness> for AccountWitnessRecord {
888 type Error = ConversionError;
889
890 fn try_from(
891 account_witness_record: proto::account::AccountWitness,
892 ) -> Result<Self, Self::Error> {
893 let witness_id = account_witness_record
894 .witness_id
895 .ok_or(proto::account::AccountWitness::missing_field(stringify!(witness_id)))?
896 .try_into()?;
897 let commitment = account_witness_record
898 .commitment
899 .ok_or(proto::account::AccountWitness::missing_field(stringify!(commitment)))?
900 .try_into()?;
901 let path: SparseMerklePath = account_witness_record
902 .path
903 .as_ref()
904 .ok_or(proto::account::AccountWitness::missing_field(stringify!(path)))?
905 .clone()
906 .try_into()?;
907
908 let witness = AccountWitness::new(witness_id, commitment, path).map_err(|err| {
909 ConversionError::deserialization_error(
910 "AccountWitness",
911 DeserializationError::InvalidValue(err.to_string()),
912 )
913 })?;
914
915 Ok(Self {
916 account_id: account_witness_record
917 .account_id
918 .ok_or(proto::account::AccountWitness::missing_field(stringify!(account_id)))?
919 .try_into()?,
920 witness,
921 })
922 }
923}
924
925impl From<AccountWitnessRecord> for proto::account::AccountWitness {
926 fn from(from: AccountWitnessRecord) -> Self {
927 Self {
928 account_id: Some(from.account_id.into()),
929 witness_id: Some(from.witness.id().into()),
930 commitment: Some(from.witness.state_commitment().into()),
931 path: Some(from.witness.path().clone().into()),
932 }
933 }
934}
935
936#[derive(Debug)]
941pub struct AccountState {
942 pub account_id: AccountId,
944 pub account_commitment: Option<Word>,
946}
947
948impl Display for AccountState {
949 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
950 f.write_fmt(format_args!(
951 "{{ account_id: {}, account_commitment: {} }}",
952 self.account_id,
953 format_opt(self.account_commitment.as_ref()),
954 ))
955 }
956}
957
958impl TryFrom<proto::store::transaction_inputs::AccountTransactionInputRecord> for AccountState {
959 type Error = ConversionError;
960
961 fn try_from(
962 from: proto::store::transaction_inputs::AccountTransactionInputRecord,
963 ) -> Result<Self, Self::Error> {
964 let account_id = from
965 .account_id
966 .ok_or(proto::store::transaction_inputs::AccountTransactionInputRecord::missing_field(
967 stringify!(account_id),
968 ))?
969 .try_into()?;
970
971 let account_commitment = from
972 .account_commitment
973 .ok_or(proto::store::transaction_inputs::AccountTransactionInputRecord::missing_field(
974 stringify!(account_commitment),
975 ))?
976 .try_into()?;
977
978 let account_commitment = if account_commitment == Word::empty() {
981 None
982 } else {
983 Some(account_commitment)
984 };
985
986 Ok(Self { account_id, account_commitment })
987 }
988}
989
990impl From<AccountState> for proto::store::transaction_inputs::AccountTransactionInputRecord {
991 fn from(from: AccountState) -> Self {
992 Self {
993 account_id: Some(from.account_id.into()),
994 account_commitment: from.account_commitment.map(Into::into),
995 }
996 }
997}
998
999impl TryFrom<proto::primitives::Asset> for Asset {
1003 type Error = ConversionError;
1004
1005 fn try_from(value: proto::primitives::Asset) -> Result<Self, Self::Error> {
1006 let inner = value.asset.ok_or(proto::primitives::Asset::missing_field("asset"))?;
1007 let word = Word::try_from(inner)?;
1008
1009 Asset::try_from(word).map_err(ConversionError::AssetError)
1010 }
1011}
1012
1013impl From<Asset> for proto::primitives::Asset {
1014 fn from(asset_from: Asset) -> Self {
1015 proto::primitives::Asset {
1016 asset: Some(Word::from(asset_from).into()),
1017 }
1018 }
1019}
1020
1021pub type AccountPrefix = u32;
1025
1026#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
1032pub struct NetworkAccountId(AccountId);
1033
1034impl std::fmt::Display for NetworkAccountId {
1035 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1036 std::fmt::Display::fmt(&self.0, f)
1037 }
1038}
1039
1040impl NetworkAccountId {
1041 pub fn inner(&self) -> AccountId {
1043 self.0
1044 }
1045
1046 pub fn prefix(&self) -> AccountPrefix {
1048 get_account_id_tag_prefix(self.0)
1049 }
1050}
1051
1052impl TryFrom<AccountId> for NetworkAccountId {
1053 type Error = NetworkAccountError;
1054
1055 fn try_from(id: AccountId) -> Result<Self, Self::Error> {
1056 if !id.is_network() {
1057 return Err(NetworkAccountError::NotNetworkAccount(id));
1058 }
1059 Ok(NetworkAccountId(id))
1060 }
1061}
1062
1063impl TryFrom<&NoteAttachment> for NetworkAccountId {
1064 type Error = NetworkAccountError;
1065
1066 fn try_from(attachment: &NoteAttachment) -> Result<Self, Self::Error> {
1067 let target = NetworkAccountTarget::try_from(attachment)
1068 .map_err(NetworkAccountError::InvalidAttachment)?;
1069 Ok(NetworkAccountId(target.target_id()))
1070 }
1071}
1072
1073impl TryFrom<NoteAttachment> for NetworkAccountId {
1074 type Error = NetworkAccountError;
1075
1076 fn try_from(attachment: NoteAttachment) -> Result<Self, Self::Error> {
1077 NetworkAccountId::try_from(&attachment)
1078 }
1079}
1080
1081impl From<NetworkAccountId> for AccountId {
1082 fn from(value: NetworkAccountId) -> Self {
1083 value.inner()
1084 }
1085}
1086
1087impl From<NetworkAccountId> for u32 {
1088 fn from(value: NetworkAccountId) -> Self {
1091 value.prefix()
1092 }
1093}
1094
1095#[derive(Debug, Error)]
1096pub enum NetworkAccountError {
1097 #[error("account ID {0} is not a valid network account ID")]
1098 NotNetworkAccount(AccountId),
1099 #[error("invalid network account attachment: {0}")]
1100 InvalidAttachment(#[source] NetworkAccountTargetError),
1101}
1102
1103fn get_account_id_tag_prefix(id: AccountId) -> AccountPrefix {
1105 (id.prefix().as_u64() >> 34) as AccountPrefix
1106}