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::serde::{Deserializable, DeserializationError, Serializable};
24use miden_standards::note::{NetworkAccountTarget, NetworkAccountTargetError};
25use thiserror::Error;
26
27use super::try_convert;
28use crate::decode;
29use crate::decode::{ConversionResultExt, GrpcDecodeExt};
30use crate::errors::ConversionError;
31use crate::generated::{self as proto};
32
33#[cfg(test)]
34mod tests;
35
36impl Display for proto::account::AccountId {
40 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
41 write!(f, "0x")?;
42 for byte in &self.id {
43 write!(f, "{byte:02x}")?;
44 }
45 Ok(())
46 }
47}
48
49impl Debug for proto::account::AccountId {
50 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
51 Display::fmt(self, f)
52 }
53}
54
55impl TryFrom<proto::account::AccountId> for AccountId {
59 type Error = ConversionError;
60
61 fn try_from(account_id: proto::account::AccountId) -> Result<Self, Self::Error> {
62 AccountId::read_from_bytes(&account_id.id)
63 .map_err(|_| ConversionError::message("value is not in the range 0..MODULUS"))
64 }
65}
66
67impl From<&AccountId> for proto::account::AccountId {
71 fn from(account_id: &AccountId) -> Self {
72 (*account_id).into()
73 }
74}
75
76impl From<AccountId> for proto::account::AccountId {
77 fn from(account_id: AccountId) -> Self {
78 Self { id: account_id.to_bytes() }
79 }
80}
81
82#[derive(Debug, PartialEq)]
86pub struct AccountSummary {
87 pub account_id: AccountId,
88 pub account_commitment: Word,
89 pub block_num: BlockNumber,
90}
91
92impl From<&AccountSummary> for proto::account::AccountSummary {
93 fn from(update: &AccountSummary) -> Self {
94 Self {
95 account_id: Some(update.account_id.into()),
96 account_commitment: Some(update.account_commitment.into()),
97 block_num: update.block_num.as_u32(),
98 }
99 }
100}
101
102#[derive(Debug, PartialEq)]
103pub struct AccountInfo {
104 pub summary: AccountSummary,
105 pub details: Option<Account>,
106}
107
108impl From<&AccountInfo> for proto::account::AccountDetails {
109 fn from(AccountInfo { summary, details }: &AccountInfo) -> Self {
110 Self {
111 summary: Some(summary.into()),
112 details: details.as_ref().map(Serializable::to_bytes),
113 }
114 }
115}
116
117impl TryFrom<proto::account::AccountStorageHeader> for AccountStorageHeader {
121 type Error = ConversionError;
122
123 fn try_from(value: proto::account::AccountStorageHeader) -> Result<Self, Self::Error> {
124 let proto::account::AccountStorageHeader { slots } = value;
125
126 let slot_headers = slots
127 .into_iter()
128 .map(|slot| {
129 let decoder = slot.decoder();
130 let slot_name = StorageSlotName::new(slot.slot_name)?;
131 let slot_type = storage_slot_type_from_raw(slot.slot_type)?;
132 let commitment = decode!(decoder, slot.commitment)?;
133 Ok(StorageSlotHeader::new(slot_name, slot_type, commitment))
134 })
135 .collect::<Result<Vec<_>, ConversionError>>()
136 .context("slots")?;
137
138 Ok(AccountStorageHeader::new(slot_headers)?)
139 }
140}
141
142pub struct AccountRequest {
147 pub account_id: AccountId,
148 pub block_num: Option<BlockNumber>,
150 pub details: Option<AccountDetailRequest>,
151}
152
153impl TryFrom<proto::rpc::AccountRequest> for AccountRequest {
154 type Error = ConversionError;
155
156 fn try_from(value: proto::rpc::AccountRequest) -> Result<Self, Self::Error> {
157 let decoder = value.decoder();
158 let proto::rpc::AccountRequest { account_id, block_num, details } = value;
159
160 let account_id = decode!(decoder, account_id)?;
161 let block_num = block_num.map(Into::into);
162
163 let details = details.map(TryFrom::try_from).transpose().context("details")?;
164
165 Ok(AccountRequest { account_id, block_num, details })
166 }
167}
168
169pub struct AccountDetailRequest {
171 pub code_commitment: Option<Word>,
172 pub asset_vault_commitment: Option<Word>,
173 pub storage_requests: Vec<StorageMapRequest>,
174}
175
176impl TryFrom<proto::rpc::account_request::AccountDetailRequest> for AccountDetailRequest {
177 type Error = ConversionError;
178
179 fn try_from(
180 value: proto::rpc::account_request::AccountDetailRequest,
181 ) -> Result<Self, Self::Error> {
182 let proto::rpc::account_request::AccountDetailRequest {
183 code_commitment,
184 asset_vault_commitment,
185 storage_maps,
186 } = value;
187
188 let code_commitment =
189 code_commitment.map(TryFrom::try_from).transpose().context("code_commitment")?;
190 let asset_vault_commitment = asset_vault_commitment
191 .map(TryFrom::try_from)
192 .transpose()
193 .context("asset_vault_commitment")?;
194 let storage_requests =
195 try_convert(storage_maps).collect::<Result<_, _>>().context("storage_maps")?;
196
197 Ok(AccountDetailRequest {
198 code_commitment,
199 asset_vault_commitment,
200 storage_requests,
201 })
202 }
203}
204
205#[derive(Debug, Clone, PartialEq, Eq)]
206pub struct StorageMapRequest {
207 pub slot_name: StorageSlotName,
208 pub slot_data: SlotData,
209}
210
211impl TryFrom<proto::rpc::account_request::account_detail_request::StorageMapDetailRequest>
212 for StorageMapRequest
213{
214 type Error = ConversionError;
215
216 fn try_from(
217 value: proto::rpc::account_request::account_detail_request::StorageMapDetailRequest,
218 ) -> Result<Self, Self::Error> {
219 let decoder = value.decoder();
220 let proto::rpc::account_request::account_detail_request::StorageMapDetailRequest {
221 slot_name,
222 slot_data,
223 } = value;
224
225 let slot_name = StorageSlotName::new(slot_name).context("slot_name")?;
226 let slot_data = decode!(decoder, slot_data)?;
227
228 Ok(StorageMapRequest { slot_name, slot_data })
229 }
230}
231
232#[derive(Debug, Clone, PartialEq, Eq)]
234pub enum SlotData {
235 All,
236 MapKeys(Vec<StorageMapKey>),
237}
238
239impl
240 TryFrom<
241 proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData,
242 > for SlotData
243{
244 type Error = ConversionError;
245
246 fn try_from(
247 value: proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData,
248 ) -> Result<Self, Self::Error> {
249 use proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData as ProtoSlotData;
250
251 Ok(match value {
252 ProtoSlotData::AllEntries(true) => SlotData::All,
253 ProtoSlotData::AllEntries(false) => {
254 return Err(ConversionError::message("enum variant discriminant out of range"));
255 },
256 ProtoSlotData::MapKeys(keys) => {
257 let keys = try_convert(keys.map_keys).collect::<Result<Vec<_>, _>>()?;
258 SlotData::MapKeys(keys)
259 },
260 })
261 }
262}
263
264impl TryFrom<proto::account::AccountHeader> for AccountHeader {
268 type Error = ConversionError;
269
270 fn try_from(value: proto::account::AccountHeader) -> Result<Self, Self::Error> {
271 let decoder = value.decoder();
272 let proto::account::AccountHeader {
273 account_id,
274 vault_root,
275 storage_commitment,
276 code_commitment,
277 nonce,
278 } = value;
279
280 let account_id = decode!(decoder, account_id)?;
281 let vault_root = decode!(decoder, vault_root)?;
282 let storage_commitment = decode!(decoder, storage_commitment)?;
283 let code_commitment = decode!(decoder, code_commitment)?;
284 let nonce = nonce
285 .try_into()
286 .map_err(|e| ConversionError::message(format!("{e}")))
287 .context("nonce")?;
288
289 Ok(AccountHeader::new(
290 account_id,
291 nonce,
292 vault_root,
293 storage_commitment,
294 code_commitment,
295 ))
296 }
297}
298
299impl From<AccountHeader> for proto::account::AccountHeader {
300 fn from(header: AccountHeader) -> Self {
301 proto::account::AccountHeader {
302 account_id: Some(header.id().into()),
303 vault_root: Some(header.vault_root().into()),
304 storage_commitment: Some(header.storage_commitment().into()),
305 code_commitment: Some(header.code_commitment().into()),
306 nonce: header.nonce().as_canonical_u64(),
307 }
308 }
309}
310
311impl From<AccountStorageHeader> for proto::account::AccountStorageHeader {
312 fn from(value: AccountStorageHeader) -> Self {
313 let slots = value
314 .slots()
315 .map(|slot_header| proto::account::account_storage_header::StorageSlot {
316 slot_name: slot_header.name().to_string(),
317 slot_type: storage_slot_type_to_raw(slot_header.slot_type()),
318 commitment: Some(proto::primitives::Digest::from(slot_header.value())),
319 })
320 .collect();
321
322 Self { slots }
323 }
324}
325
326#[derive(Debug, Clone, PartialEq, Eq)]
336pub enum AccountVaultDetails {
337 LimitExceeded,
340
341 Assets(Vec<Asset>),
343}
344
345impl AccountVaultDetails {
346 pub const MAX_RETURN_ENTRIES: usize = 1000;
349
350 pub fn new(vault: &AssetVault) -> Self {
351 if vault.assets().nth(Self::MAX_RETURN_ENTRIES).is_some() {
352 Self::LimitExceeded
353 } else {
354 Self::Assets(Vec::from_iter(vault.assets()))
355 }
356 }
357
358 pub fn empty() -> Self {
359 Self::Assets(Vec::new())
360 }
361
362 pub fn from_assets(assets: Vec<Asset>) -> Self {
364 if assets.len() > Self::MAX_RETURN_ENTRIES {
365 Self::LimitExceeded
366 } else {
367 Self::Assets(assets)
368 }
369 }
370}
371
372impl TryFrom<proto::rpc::AccountVaultDetails> for AccountVaultDetails {
373 type Error = ConversionError;
374
375 fn try_from(value: proto::rpc::AccountVaultDetails) -> Result<Self, Self::Error> {
376 let proto::rpc::AccountVaultDetails { too_many_assets, assets } = value;
377
378 if too_many_assets {
379 Ok(Self::LimitExceeded)
380 } else {
381 let parsed_assets = Result::<Vec<_>, ConversionError>::from_iter(
382 assets.into_iter().map(Asset::try_from),
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(proto::primitives::Asset::from)),
399 },
400 }
401 }
402}
403
404#[derive(Debug, Clone, PartialEq, Eq)]
409pub struct AccountStorageMapDetails {
410 pub slot_name: StorageSlotName,
411 pub entries: StorageMapEntries,
412}
413
414#[derive(Debug, Clone, PartialEq, Eq)]
421pub enum StorageMapEntries {
422 LimitExceeded,
425
426 AllEntries(Vec<(StorageMapKey, Word)>),
429
430 EntriesWithProofs(Vec<SmtProof>),
433}
434
435impl AccountStorageMapDetails {
436 pub const MAX_RETURN_ENTRIES: usize = 1000;
438
439 pub const MAX_SMT_PROOF_ENTRIES: usize = QueryParamStorageMapKeyTotalLimit::LIMIT;
447
448 pub fn from_all_entries(slot_name: StorageSlotName, storage_map: &StorageMap) -> Self {
453 if storage_map.num_entries() > Self::MAX_RETURN_ENTRIES {
454 Self {
455 slot_name,
456 entries: StorageMapEntries::LimitExceeded,
457 }
458 } else {
459 let entries = Vec::from_iter(storage_map.entries().map(|(k, v)| (*k, *v)));
460 Self {
461 slot_name,
462 entries: StorageMapEntries::AllEntries(entries),
463 }
464 }
465 }
466
467 pub fn from_forest_entries(
471 slot_name: StorageSlotName,
472 entries: Vec<(StorageMapKey, Word)>,
473 ) -> Self {
474 if entries.len() > Self::MAX_RETURN_ENTRIES {
475 Self {
476 slot_name,
477 entries: StorageMapEntries::LimitExceeded,
478 }
479 } else {
480 Self {
481 slot_name,
482 entries: StorageMapEntries::AllEntries(entries),
483 }
484 }
485 }
486
487 pub fn from_proofs(slot_name: StorageSlotName, proofs: Vec<SmtProof>) -> Self {
492 if proofs.len() > Self::MAX_SMT_PROOF_ENTRIES {
493 Self {
494 slot_name,
495 entries: StorageMapEntries::LimitExceeded,
496 }
497 } else {
498 Self {
499 slot_name,
500 entries: StorageMapEntries::EntriesWithProofs(proofs),
501 }
502 }
503 }
504
505 pub fn limit_exceeded(slot_name: StorageSlotName) -> Self {
507 Self {
508 slot_name,
509 entries: StorageMapEntries::LimitExceeded,
510 }
511 }
512}
513
514impl TryFrom<proto::rpc::account_storage_details::AccountStorageMapDetails>
515 for AccountStorageMapDetails
516{
517 type Error = ConversionError;
518
519 fn try_from(
520 value: proto::rpc::account_storage_details::AccountStorageMapDetails,
521 ) -> Result<Self, Self::Error> {
522 use proto::rpc::account_storage_details::account_storage_map_details::{
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).context("slot_name")?;
535
536 let entries = if too_many_entries {
537 StorageMapEntries::LimitExceeded
538 } else {
539 match entries {
540 None => {
541 return Err(ConversionError::missing_field::<
542 proto::rpc::account_storage_details::AccountStorageMapDetails,
543 >("entries"));
544 },
545 Some(ProtoEntries::AllEntries(AllMapEntries { entries })) => {
546 let entries = entries
547 .into_iter()
548 .map(|entry| {
549 let decoder = entry.decoder();
550 let key = StorageMapKey::new(decode!(decoder, entry.key)?);
551 let value = decode!(decoder, entry.value)?;
552 Ok((key, value))
553 })
554 .collect::<Result<Vec<_>, ConversionError>>()
555 .context("entries")?;
556 StorageMapEntries::AllEntries(entries)
557 },
558 Some(ProtoEntries::EntriesWithProofs(MapEntriesWithProofs { entries })) => {
559 let proofs = entries
560 .into_iter()
561 .map(|entry| {
562 let decoder = entry.decoder();
563 decode!(decoder, entry.proof)
564 })
565 .collect::<Result<Vec<_>, ConversionError>>()
566 .context("entries")?;
567 StorageMapEntries::EntriesWithProofs(proofs)
568 },
569 }
570 };
571
572 Ok(Self { slot_name, entries })
573 }
574}
575
576impl From<AccountStorageMapDetails>
577 for proto::rpc::account_storage_details::AccountStorageMapDetails
578{
579 fn from(value: AccountStorageMapDetails) -> Self {
580 use proto::rpc::account_storage_details::account_storage_map_details::{
581 AllMapEntries,
582 Entries as ProtoEntries,
583 MapEntriesWithProofs,
584 };
585
586 let AccountStorageMapDetails { slot_name, entries } = value;
587
588 let (too_many_entries, proto_entries) = match entries {
589 StorageMapEntries::LimitExceeded => (true, None),
590 StorageMapEntries::AllEntries(entries) => {
591 let all = AllMapEntries {
592 entries: Vec::from_iter(entries.into_iter().map(|(key, value)| {
593 proto::rpc::account_storage_details::account_storage_map_details::all_map_entries::StorageMapEntry {
594 key: Some(key.into()),
595 value: Some(value.into()),
596 }
597 })),
598 };
599 (false, Some(ProtoEntries::AllEntries(all)))
600 },
601 StorageMapEntries::EntriesWithProofs(proofs) => {
602 use miden_protocol::crypto::merkle::smt::SmtLeaf;
603
604 let with_proofs = MapEntriesWithProofs {
605 entries: Vec::from_iter(proofs.into_iter().map(|proof| {
606 let (key, value) = match proof.leaf() {
608 SmtLeaf::Empty(_) => {
609 (miden_protocol::EMPTY_WORD, miden_protocol::EMPTY_WORD)
610 },
611 SmtLeaf::Single((k, v)) => (*k, *v),
612 SmtLeaf::Multiple(entries) => entries.iter().next().map_or(
613 (miden_protocol::EMPTY_WORD, miden_protocol::EMPTY_WORD),
614 |(k, v)| (*k, *v),
615 ),
616 };
617 let smt_opening = proto::primitives::SmtOpening::from(proof);
618 proto::rpc::account_storage_details::account_storage_map_details::map_entries_with_proofs::StorageMapEntryWithProof {
619 key: Some(key.into()),
620 value: Some(value.into()),
621 proof: Some(smt_opening),
622 }
623 })),
624 };
625 (false, Some(ProtoEntries::EntriesWithProofs(with_proofs)))
626 },
627 };
628
629 Self {
630 slot_name: slot_name.to_string(),
631 too_many_entries,
632 entries: proto_entries,
633 }
634 }
635}
636
637#[derive(Debug, Clone, PartialEq)]
638pub struct AccountStorageDetails {
639 pub header: AccountStorageHeader,
640 pub map_details: Vec<AccountStorageMapDetails>,
641}
642
643impl AccountStorageDetails {
644 pub fn all_limits_exceeded(
646 header: AccountStorageHeader,
647 slot_names: impl IntoIterator<Item = StorageSlotName>,
648 ) -> Self {
649 Self {
650 header,
651 map_details: Vec::from_iter(
652 slot_names.into_iter().map(AccountStorageMapDetails::limit_exceeded),
653 ),
654 }
655 }
656}
657
658impl TryFrom<proto::rpc::AccountStorageDetails> for AccountStorageDetails {
659 type Error = ConversionError;
660
661 fn try_from(value: proto::rpc::AccountStorageDetails) -> Result<Self, Self::Error> {
662 let decoder = value.decoder();
663 let proto::rpc::AccountStorageDetails { header, map_details } = value;
664
665 let header = decode!(decoder, header)?;
666
667 let map_details =
668 try_convert(map_details).collect::<Result<Vec<_>, _>>().context("map_details")?;
669
670 Ok(Self { header, map_details })
671 }
672}
673
674impl From<AccountStorageDetails> for proto::rpc::AccountStorageDetails {
675 fn from(value: AccountStorageDetails) -> Self {
676 let AccountStorageDetails { header, map_details } = value;
677
678 Self {
679 header: Some(header.into()),
680 map_details: map_details.into_iter().map(Into::into).collect(),
681 }
682 }
683}
684
685fn storage_slot_type_from_raw(slot_type: u32) -> Result<StorageSlotType, ConversionError> {
686 Ok(match slot_type {
687 0 => StorageSlotType::Value,
688 1 => StorageSlotType::Map,
689 _ => return Err(ConversionError::message("enum variant discriminant out of range")),
690 })
691}
692
693const fn storage_slot_type_to_raw(slot_type: StorageSlotType) -> u32 {
694 match slot_type {
695 StorageSlotType::Value => 0,
696 StorageSlotType::Map => 1,
697 }
698}
699
700pub struct AccountResponse {
705 pub block_num: BlockNumber,
706 pub witness: AccountWitness,
707 pub details: Option<AccountDetails>,
708}
709
710impl TryFrom<proto::rpc::AccountResponse> for AccountResponse {
711 type Error = ConversionError;
712
713 fn try_from(value: proto::rpc::AccountResponse) -> Result<Self, Self::Error> {
714 let decoder = value.decoder();
715 let proto::rpc::AccountResponse { block_num, witness, details } = value;
716
717 let block_num = block_num
718 .ok_or(ConversionError::missing_field::<proto::rpc::AccountResponse>("block_num"))?
719 .into();
720
721 let witness = decode!(decoder, witness)?;
722
723 let details = details.map(TryFrom::try_from).transpose().context("details")?;
724
725 Ok(AccountResponse { block_num, witness, details })
726 }
727}
728
729impl From<AccountResponse> for proto::rpc::AccountResponse {
730 fn from(value: AccountResponse) -> Self {
731 let AccountResponse { block_num, witness, details } = value;
732
733 Self {
734 witness: Some(witness.into()),
735 details: details.map(Into::into),
736 block_num: Some(block_num.into()),
737 }
738 }
739}
740
741pub struct AccountDetails {
746 pub account_header: AccountHeader,
747 pub account_code: Option<Vec<u8>>,
748 pub vault_details: AccountVaultDetails,
749 pub storage_details: AccountStorageDetails,
750}
751
752impl AccountDetails {
753 pub fn with_storage_limits_exceeded(
755 account_header: AccountHeader,
756 account_code: Option<Vec<u8>>,
757 vault_details: AccountVaultDetails,
758 storage_header: AccountStorageHeader,
759 slot_names: impl IntoIterator<Item = StorageSlotName>,
760 ) -> Self {
761 Self {
762 account_header,
763 account_code,
764 vault_details,
765 storage_details: AccountStorageDetails::all_limits_exceeded(storage_header, slot_names),
766 }
767 }
768}
769
770impl TryFrom<proto::rpc::account_response::AccountDetails> for AccountDetails {
771 type Error = ConversionError;
772
773 fn try_from(value: proto::rpc::account_response::AccountDetails) -> Result<Self, Self::Error> {
774 let decoder = value.decoder();
775 let proto::rpc::account_response::AccountDetails {
776 header,
777 code,
778 vault_details,
779 storage_details,
780 } = value;
781
782 let account_header = decode!(decoder, header)?;
783
784 let storage_details = decode!(decoder, storage_details)?;
785
786 let vault_details = decode!(decoder, vault_details)?;
787 let account_code = code;
788
789 Ok(AccountDetails {
790 account_header,
791 account_code,
792 vault_details,
793 storage_details,
794 })
795 }
796}
797
798impl From<AccountDetails> for proto::rpc::account_response::AccountDetails {
799 fn from(value: AccountDetails) -> Self {
800 let AccountDetails {
801 account_header,
802 storage_details,
803 account_code,
804 vault_details,
805 } = value;
806
807 let header = Some(proto::account::AccountHeader::from(account_header));
808 let storage_details = Some(storage_details.into());
809 let code = account_code;
810 let vault_details = Some(vault_details.into());
811
812 Self {
813 header,
814 storage_details,
815 code,
816 vault_details,
817 }
818 }
819}
820
821impl TryFrom<proto::account::AccountWitness> for AccountWitness {
825 type Error = ConversionError;
826
827 fn try_from(account_witness: proto::account::AccountWitness) -> Result<Self, Self::Error> {
828 let decoder = account_witness.decoder();
829 let witness_id = decode!(decoder, account_witness.witness_id)?;
830 let commitment = decode!(decoder, account_witness.commitment)?;
831 let path = decode!(decoder, account_witness.path)?;
832
833 AccountWitness::new(witness_id, commitment, path).map_err(|err| {
834 ConversionError::deserialization(
835 "AccountWitness",
836 DeserializationError::InvalidValue(err.to_string()),
837 )
838 })
839 }
840}
841
842impl From<AccountWitness> for proto::account::AccountWitness {
843 fn from(witness: AccountWitness) -> Self {
844 Self {
845 account_id: Some(witness.id().into()),
846 witness_id: Some(witness.id().into()),
847 commitment: Some(witness.state_commitment().into()),
848 path: Some(witness.into_proof().into_parts().0.into()),
849 }
850 }
851}
852
853#[derive(Clone, Debug, PartialEq, Eq)]
857pub struct AccountWitnessRecord {
858 pub account_id: AccountId,
859 pub witness: AccountWitness,
860}
861
862impl TryFrom<proto::account::AccountWitness> for AccountWitnessRecord {
863 type Error = ConversionError;
864
865 fn try_from(
866 account_witness_record: proto::account::AccountWitness,
867 ) -> Result<Self, Self::Error> {
868 let decoder = account_witness_record.decoder();
869 let witness_id = decode!(decoder, account_witness_record.witness_id)?;
870 let commitment = decode!(decoder, account_witness_record.commitment)?;
871 let account_id = decode!(decoder, account_witness_record.account_id)?;
872 let path: SparseMerklePath = decode!(decoder, account_witness_record.path)?;
873
874 let witness = AccountWitness::new(witness_id, commitment, path).map_err(|err| {
875 ConversionError::deserialization(
876 "AccountWitness",
877 DeserializationError::InvalidValue(err.to_string()),
878 )
879 })?;
880
881 Ok(Self { account_id, witness })
882 }
883}
884
885impl From<AccountWitnessRecord> for proto::account::AccountWitness {
886 fn from(from: AccountWitnessRecord) -> Self {
887 Self {
888 account_id: Some(from.account_id.into()),
889 witness_id: Some(from.witness.id().into()),
890 commitment: Some(from.witness.state_commitment().into()),
891 path: Some(from.witness.path().clone().into()),
892 }
893 }
894}
895
896#[derive(Debug)]
901pub struct AccountState {
902 pub account_id: AccountId,
904 pub account_commitment: Option<Word>,
906}
907
908impl Display for AccountState {
909 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
910 f.write_fmt(format_args!(
911 "{{ account_id: {}, account_commitment: {} }}",
912 self.account_id,
913 format_opt(self.account_commitment.as_ref()),
914 ))
915 }
916}
917
918impl TryFrom<proto::store::transaction_inputs::AccountTransactionInputRecord> for AccountState {
919 type Error = ConversionError;
920
921 fn try_from(
922 from: proto::store::transaction_inputs::AccountTransactionInputRecord,
923 ) -> Result<Self, Self::Error> {
924 let decoder = from.decoder();
925 let account_id = decode!(decoder, from.account_id)?;
926
927 let account_commitment = decode!(decoder, from.account_commitment)?;
928
929 let account_commitment = if account_commitment == Word::empty() {
932 None
933 } else {
934 Some(account_commitment)
935 };
936
937 Ok(Self { account_id, account_commitment })
938 }
939}
940
941impl From<AccountState> for proto::store::transaction_inputs::AccountTransactionInputRecord {
942 fn from(from: AccountState) -> Self {
943 Self {
944 account_id: Some(from.account_id.into()),
945 account_commitment: from.account_commitment.map(Into::into),
946 }
947 }
948}
949
950impl TryFrom<proto::primitives::Asset> for Asset {
954 type Error = ConversionError;
955
956 fn try_from(asset: proto::primitives::Asset) -> Result<Self, Self::Error> {
957 let decoder = asset.decoder();
958 let key_word: Word = decode!(decoder, asset.key)?;
959 let value_word: Word = decode!(decoder, asset.value)?;
960
961 let asset = Asset::from_key_value_words(key_word, value_word)?;
962 Ok(asset)
963 }
964}
965
966impl From<Asset> for proto::primitives::Asset {
967 fn from(asset_from: Asset) -> Self {
968 proto::primitives::Asset {
969 key: Some(asset_from.to_key_word().into()),
970 value: Some(asset_from.to_value_word().into()),
971 }
972 }
973}
974
975pub type AccountPrefix = u32;
979
980#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
986pub struct NetworkAccountId(AccountId);
987
988impl std::fmt::Display for NetworkAccountId {
989 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
990 std::fmt::Display::fmt(&self.0, f)
991 }
992}
993
994impl NetworkAccountId {
995 pub fn inner(&self) -> AccountId {
997 self.0
998 }
999
1000 pub fn prefix(&self) -> AccountPrefix {
1002 get_account_id_tag_prefix(self.0)
1003 }
1004}
1005
1006impl TryFrom<AccountId> for NetworkAccountId {
1007 type Error = NetworkAccountError;
1008
1009 fn try_from(id: AccountId) -> Result<Self, Self::Error> {
1010 if !id.is_network() {
1011 return Err(NetworkAccountError::NotNetworkAccount(id));
1012 }
1013 Ok(NetworkAccountId(id))
1014 }
1015}
1016
1017impl TryFrom<&NoteAttachment> for NetworkAccountId {
1018 type Error = NetworkAccountError;
1019
1020 fn try_from(attachment: &NoteAttachment) -> Result<Self, Self::Error> {
1021 let target = NetworkAccountTarget::try_from(attachment)
1022 .map_err(NetworkAccountError::InvalidAttachment)?;
1023 Ok(NetworkAccountId(target.target_id()))
1024 }
1025}
1026
1027impl TryFrom<NoteAttachment> for NetworkAccountId {
1028 type Error = NetworkAccountError;
1029
1030 fn try_from(attachment: NoteAttachment) -> Result<Self, Self::Error> {
1031 NetworkAccountId::try_from(&attachment)
1032 }
1033}
1034
1035impl From<NetworkAccountId> for AccountId {
1036 fn from(value: NetworkAccountId) -> Self {
1037 value.inner()
1038 }
1039}
1040
1041impl From<NetworkAccountId> for u32 {
1042 fn from(value: NetworkAccountId) -> Self {
1045 value.prefix()
1046 }
1047}
1048
1049#[derive(Debug, Error)]
1050pub enum NetworkAccountError {
1051 #[error("account ID {0} is not a valid network account ID")]
1052 NotNetworkAccount(AccountId),
1053 #[error("invalid network account attachment: {0}")]
1054 InvalidAttachment(#[source] NetworkAccountTargetError),
1055}
1056
1057fn get_account_id_tag_prefix(id: AccountId) -> AccountPrefix {
1059 (id.prefix().as_u64() >> 34) as AccountPrefix
1060}