use std::fmt::{Debug, Display, Formatter};
use miden_node_utils::formatting::format_opt;
use miden_node_utils::limiter::{QueryParamLimiter, QueryParamStorageMapKeyTotalLimit};
use miden_protocol::Word;
use miden_protocol::account::{
Account,
AccountHeader,
AccountId,
AccountStorageHeader,
StorageMap,
StorageMapKey,
StorageSlotHeader,
StorageSlotName,
StorageSlotType,
};
use miden_protocol::asset::{Asset, AssetVault};
use miden_protocol::block::BlockNumber;
use miden_protocol::block::account_tree::AccountWitness;
use miden_protocol::crypto::merkle::SparseMerklePath;
use miden_protocol::crypto::merkle::smt::SmtProof;
use miden_protocol::note::NoteAttachment;
use miden_protocol::utils::serde::{Deserializable, DeserializationError, Serializable};
use miden_standards::note::{NetworkAccountTarget, NetworkAccountTargetError};
use thiserror::Error;
use super::try_convert;
use crate::decode;
use crate::decode::{ConversionResultExt, GrpcDecodeExt};
use crate::errors::ConversionError;
use crate::generated::{self as proto};
#[cfg(test)]
mod tests;
impl Display for proto::account::AccountId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "0x")?;
for byte in &self.id {
write!(f, "{byte:02x}")?;
}
Ok(())
}
}
impl Debug for proto::account::AccountId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl TryFrom<proto::account::AccountId> for AccountId {
type Error = ConversionError;
fn try_from(account_id: proto::account::AccountId) -> Result<Self, Self::Error> {
AccountId::read_from_bytes(&account_id.id)
.map_err(|_| ConversionError::message("value is not in the range 0..MODULUS"))
}
}
impl From<&AccountId> for proto::account::AccountId {
fn from(account_id: &AccountId) -> Self {
(*account_id).into()
}
}
impl From<AccountId> for proto::account::AccountId {
fn from(account_id: AccountId) -> Self {
Self { id: account_id.to_bytes() }
}
}
#[derive(Debug, PartialEq)]
pub struct AccountSummary {
pub account_id: AccountId,
pub account_commitment: Word,
pub block_num: BlockNumber,
}
impl From<&AccountSummary> for proto::account::AccountSummary {
fn from(update: &AccountSummary) -> Self {
Self {
account_id: Some(update.account_id.into()),
account_commitment: Some(update.account_commitment.into()),
block_num: update.block_num.as_u32(),
}
}
}
#[derive(Debug, PartialEq)]
pub struct AccountInfo {
pub summary: AccountSummary,
pub details: Option<Account>,
}
impl From<&AccountInfo> for proto::account::AccountDetails {
fn from(AccountInfo { summary, details }: &AccountInfo) -> Self {
Self {
summary: Some(summary.into()),
details: details.as_ref().map(Serializable::to_bytes),
}
}
}
impl TryFrom<proto::account::AccountStorageHeader> for AccountStorageHeader {
type Error = ConversionError;
fn try_from(value: proto::account::AccountStorageHeader) -> Result<Self, Self::Error> {
let proto::account::AccountStorageHeader { slots } = value;
let slot_headers = slots
.into_iter()
.map(|slot| {
let decoder = slot.decoder();
let slot_name = StorageSlotName::new(slot.slot_name)?;
let slot_type = storage_slot_type_from_raw(slot.slot_type)?;
let commitment = decode!(decoder, slot.commitment)?;
Ok(StorageSlotHeader::new(slot_name, slot_type, commitment))
})
.collect::<Result<Vec<_>, ConversionError>>()
.context("slots")?;
Ok(AccountStorageHeader::new(slot_headers)?)
}
}
pub struct AccountRequest {
pub account_id: AccountId,
pub block_num: Option<BlockNumber>,
pub details: Option<AccountDetailRequest>,
}
impl TryFrom<proto::rpc::AccountRequest> for AccountRequest {
type Error = ConversionError;
fn try_from(value: proto::rpc::AccountRequest) -> Result<Self, Self::Error> {
let decoder = value.decoder();
let proto::rpc::AccountRequest { account_id, block_num, details } = value;
let account_id = decode!(decoder, account_id)?;
let block_num = block_num.map(Into::into);
let details = details.map(TryFrom::try_from).transpose().context("details")?;
Ok(AccountRequest { account_id, block_num, details })
}
}
pub struct AccountDetailRequest {
pub code_commitment: Option<Word>,
pub asset_vault_commitment: Option<Word>,
pub storage_requests: Vec<StorageMapRequest>,
}
impl TryFrom<proto::rpc::account_request::AccountDetailRequest> for AccountDetailRequest {
type Error = ConversionError;
fn try_from(
value: proto::rpc::account_request::AccountDetailRequest,
) -> Result<Self, Self::Error> {
let proto::rpc::account_request::AccountDetailRequest {
code_commitment,
asset_vault_commitment,
storage_maps,
} = value;
let code_commitment =
code_commitment.map(TryFrom::try_from).transpose().context("code_commitment")?;
let asset_vault_commitment = asset_vault_commitment
.map(TryFrom::try_from)
.transpose()
.context("asset_vault_commitment")?;
let storage_requests =
try_convert(storage_maps).collect::<Result<_, _>>().context("storage_maps")?;
Ok(AccountDetailRequest {
code_commitment,
asset_vault_commitment,
storage_requests,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StorageMapRequest {
pub slot_name: StorageSlotName,
pub slot_data: SlotData,
}
impl TryFrom<proto::rpc::account_request::account_detail_request::StorageMapDetailRequest>
for StorageMapRequest
{
type Error = ConversionError;
fn try_from(
value: proto::rpc::account_request::account_detail_request::StorageMapDetailRequest,
) -> Result<Self, Self::Error> {
let decoder = value.decoder();
let proto::rpc::account_request::account_detail_request::StorageMapDetailRequest {
slot_name,
slot_data,
} = value;
let slot_name = StorageSlotName::new(slot_name).context("slot_name")?;
let slot_data = decode!(decoder, slot_data)?;
Ok(StorageMapRequest { slot_name, slot_data })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SlotData {
All,
MapKeys(Vec<StorageMapKey>),
}
impl
TryFrom<
proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData,
> for SlotData
{
type Error = ConversionError;
fn try_from(
value: proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData,
) -> Result<Self, Self::Error> {
use proto::rpc::account_request::account_detail_request::storage_map_detail_request::SlotData as ProtoSlotData;
Ok(match value {
ProtoSlotData::AllEntries(true) => SlotData::All,
ProtoSlotData::AllEntries(false) => {
return Err(ConversionError::message("enum variant discriminant out of range"));
},
ProtoSlotData::MapKeys(keys) => {
let keys = try_convert(keys.map_keys).collect::<Result<Vec<_>, _>>()?;
SlotData::MapKeys(keys)
},
})
}
}
impl TryFrom<proto::account::AccountHeader> for AccountHeader {
type Error = ConversionError;
fn try_from(value: proto::account::AccountHeader) -> Result<Self, Self::Error> {
let decoder = value.decoder();
let proto::account::AccountHeader {
account_id,
vault_root,
storage_commitment,
code_commitment,
nonce,
} = value;
let account_id = decode!(decoder, account_id)?;
let vault_root = decode!(decoder, vault_root)?;
let storage_commitment = decode!(decoder, storage_commitment)?;
let code_commitment = decode!(decoder, code_commitment)?;
let nonce = nonce
.try_into()
.map_err(|e| ConversionError::message(format!("{e}")))
.context("nonce")?;
Ok(AccountHeader::new(
account_id,
nonce,
vault_root,
storage_commitment,
code_commitment,
))
}
}
impl From<AccountHeader> for proto::account::AccountHeader {
fn from(header: AccountHeader) -> Self {
proto::account::AccountHeader {
account_id: Some(header.id().into()),
vault_root: Some(header.vault_root().into()),
storage_commitment: Some(header.storage_commitment().into()),
code_commitment: Some(header.code_commitment().into()),
nonce: header.nonce().as_canonical_u64(),
}
}
}
impl From<AccountStorageHeader> for proto::account::AccountStorageHeader {
fn from(value: AccountStorageHeader) -> Self {
let slots = value
.slots()
.map(|slot_header| proto::account::account_storage_header::StorageSlot {
slot_name: slot_header.name().to_string(),
slot_type: storage_slot_type_to_raw(slot_header.slot_type()),
commitment: Some(proto::primitives::Digest::from(slot_header.value())),
})
.collect();
Self { slots }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AccountVaultDetails {
LimitExceeded,
Assets(Vec<Asset>),
}
impl AccountVaultDetails {
pub const MAX_RETURN_ENTRIES: usize = 1000;
pub fn new(vault: &AssetVault) -> Self {
if vault.assets().nth(Self::MAX_RETURN_ENTRIES).is_some() {
Self::LimitExceeded
} else {
Self::Assets(Vec::from_iter(vault.assets()))
}
}
pub fn empty() -> Self {
Self::Assets(Vec::new())
}
pub fn from_assets(assets: Vec<Asset>) -> Self {
if assets.len() > Self::MAX_RETURN_ENTRIES {
Self::LimitExceeded
} else {
Self::Assets(assets)
}
}
}
impl TryFrom<proto::rpc::AccountVaultDetails> for AccountVaultDetails {
type Error = ConversionError;
fn try_from(value: proto::rpc::AccountVaultDetails) -> Result<Self, Self::Error> {
let proto::rpc::AccountVaultDetails { too_many_assets, assets } = value;
if too_many_assets {
Ok(Self::LimitExceeded)
} else {
let parsed_assets = Result::<Vec<_>, ConversionError>::from_iter(
assets.into_iter().map(Asset::try_from),
)?;
Ok(Self::Assets(parsed_assets))
}
}
}
impl From<AccountVaultDetails> for proto::rpc::AccountVaultDetails {
fn from(value: AccountVaultDetails) -> Self {
match value {
AccountVaultDetails::LimitExceeded => Self {
too_many_assets: true,
assets: Vec::new(),
},
AccountVaultDetails::Assets(assets) => Self {
too_many_assets: false,
assets: Vec::from_iter(assets.into_iter().map(proto::primitives::Asset::from)),
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccountStorageMapDetails {
pub slot_name: StorageSlotName,
pub entries: StorageMapEntries,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StorageMapEntries {
LimitExceeded,
AllEntries(Vec<(StorageMapKey, Word)>),
EntriesWithProofs(Vec<SmtProof>),
}
impl AccountStorageMapDetails {
pub const MAX_RETURN_ENTRIES: usize = 1000;
pub const MAX_SMT_PROOF_ENTRIES: usize = QueryParamStorageMapKeyTotalLimit::LIMIT;
pub fn from_all_entries(slot_name: StorageSlotName, storage_map: &StorageMap) -> Self {
if storage_map.num_entries() > Self::MAX_RETURN_ENTRIES {
Self {
slot_name,
entries: StorageMapEntries::LimitExceeded,
}
} else {
let entries = Vec::from_iter(storage_map.entries().map(|(k, v)| (*k, *v)));
Self {
slot_name,
entries: StorageMapEntries::AllEntries(entries),
}
}
}
pub fn from_forest_entries(
slot_name: StorageSlotName,
entries: Vec<(StorageMapKey, Word)>,
) -> Self {
if entries.len() > Self::MAX_RETURN_ENTRIES {
Self {
slot_name,
entries: StorageMapEntries::LimitExceeded,
}
} else {
Self {
slot_name,
entries: StorageMapEntries::AllEntries(entries),
}
}
}
pub fn from_proofs(slot_name: StorageSlotName, proofs: Vec<SmtProof>) -> Self {
if proofs.len() > Self::MAX_SMT_PROOF_ENTRIES {
Self {
slot_name,
entries: StorageMapEntries::LimitExceeded,
}
} else {
Self {
slot_name,
entries: StorageMapEntries::EntriesWithProofs(proofs),
}
}
}
pub fn limit_exceeded(slot_name: StorageSlotName) -> Self {
Self {
slot_name,
entries: StorageMapEntries::LimitExceeded,
}
}
}
impl TryFrom<proto::rpc::account_storage_details::AccountStorageMapDetails>
for AccountStorageMapDetails
{
type Error = ConversionError;
fn try_from(
value: proto::rpc::account_storage_details::AccountStorageMapDetails,
) -> Result<Self, Self::Error> {
use proto::rpc::account_storage_details::account_storage_map_details::{
AllMapEntries,
Entries as ProtoEntries,
MapEntriesWithProofs,
};
let proto::rpc::account_storage_details::AccountStorageMapDetails {
slot_name,
too_many_entries,
entries,
} = value;
let slot_name = StorageSlotName::new(slot_name).context("slot_name")?;
let entries = if too_many_entries {
StorageMapEntries::LimitExceeded
} else {
match entries {
None => {
return Err(ConversionError::missing_field::<
proto::rpc::account_storage_details::AccountStorageMapDetails,
>("entries"));
},
Some(ProtoEntries::AllEntries(AllMapEntries { entries })) => {
let entries = entries
.into_iter()
.map(|entry| {
let decoder = entry.decoder();
let key = StorageMapKey::new(decode!(decoder, entry.key)?);
let value = decode!(decoder, entry.value)?;
Ok((key, value))
})
.collect::<Result<Vec<_>, ConversionError>>()
.context("entries")?;
StorageMapEntries::AllEntries(entries)
},
Some(ProtoEntries::EntriesWithProofs(MapEntriesWithProofs { entries })) => {
let proofs = entries
.into_iter()
.map(|entry| {
let decoder = entry.decoder();
decode!(decoder, entry.proof)
})
.collect::<Result<Vec<_>, ConversionError>>()
.context("entries")?;
StorageMapEntries::EntriesWithProofs(proofs)
},
}
};
Ok(Self { slot_name, entries })
}
}
impl From<AccountStorageMapDetails>
for proto::rpc::account_storage_details::AccountStorageMapDetails
{
fn from(value: AccountStorageMapDetails) -> Self {
use proto::rpc::account_storage_details::account_storage_map_details::{
AllMapEntries,
Entries as ProtoEntries,
MapEntriesWithProofs,
};
let AccountStorageMapDetails { slot_name, entries } = value;
let (too_many_entries, proto_entries) = match entries {
StorageMapEntries::LimitExceeded => (true, None),
StorageMapEntries::AllEntries(entries) => {
let all = AllMapEntries {
entries: Vec::from_iter(entries.into_iter().map(|(key, value)| {
proto::rpc::account_storage_details::account_storage_map_details::all_map_entries::StorageMapEntry {
key: Some(key.into()),
value: Some(value.into()),
}
})),
};
(false, Some(ProtoEntries::AllEntries(all)))
},
StorageMapEntries::EntriesWithProofs(proofs) => {
use miden_protocol::crypto::merkle::smt::SmtLeaf;
let with_proofs = MapEntriesWithProofs {
entries: Vec::from_iter(proofs.into_iter().map(|proof| {
let (key, value) = match proof.leaf() {
SmtLeaf::Empty(_) => {
(miden_protocol::EMPTY_WORD, miden_protocol::EMPTY_WORD)
},
SmtLeaf::Single((k, v)) => (*k, *v),
SmtLeaf::Multiple(entries) => entries.iter().next().map_or(
(miden_protocol::EMPTY_WORD, miden_protocol::EMPTY_WORD),
|(k, v)| (*k, *v),
),
};
let smt_opening = proto::primitives::SmtOpening::from(proof);
proto::rpc::account_storage_details::account_storage_map_details::map_entries_with_proofs::StorageMapEntryWithProof {
key: Some(key.into()),
value: Some(value.into()),
proof: Some(smt_opening),
}
})),
};
(false, Some(ProtoEntries::EntriesWithProofs(with_proofs)))
},
};
Self {
slot_name: slot_name.to_string(),
too_many_entries,
entries: proto_entries,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AccountStorageDetails {
pub header: AccountStorageHeader,
pub map_details: Vec<AccountStorageMapDetails>,
}
impl AccountStorageDetails {
pub fn all_limits_exceeded(
header: AccountStorageHeader,
slot_names: impl IntoIterator<Item = StorageSlotName>,
) -> Self {
Self {
header,
map_details: Vec::from_iter(
slot_names.into_iter().map(AccountStorageMapDetails::limit_exceeded),
),
}
}
}
impl TryFrom<proto::rpc::AccountStorageDetails> for AccountStorageDetails {
type Error = ConversionError;
fn try_from(value: proto::rpc::AccountStorageDetails) -> Result<Self, Self::Error> {
let decoder = value.decoder();
let proto::rpc::AccountStorageDetails { header, map_details } = value;
let header = decode!(decoder, header)?;
let map_details =
try_convert(map_details).collect::<Result<Vec<_>, _>>().context("map_details")?;
Ok(Self { header, map_details })
}
}
impl From<AccountStorageDetails> for proto::rpc::AccountStorageDetails {
fn from(value: AccountStorageDetails) -> Self {
let AccountStorageDetails { header, map_details } = value;
Self {
header: Some(header.into()),
map_details: map_details.into_iter().map(Into::into).collect(),
}
}
}
fn storage_slot_type_from_raw(slot_type: u32) -> Result<StorageSlotType, ConversionError> {
Ok(match slot_type {
0 => StorageSlotType::Value,
1 => StorageSlotType::Map,
_ => return Err(ConversionError::message("enum variant discriminant out of range")),
})
}
const fn storage_slot_type_to_raw(slot_type: StorageSlotType) -> u32 {
match slot_type {
StorageSlotType::Value => 0,
StorageSlotType::Map => 1,
}
}
pub struct AccountResponse {
pub block_num: BlockNumber,
pub witness: AccountWitness,
pub details: Option<AccountDetails>,
}
impl TryFrom<proto::rpc::AccountResponse> for AccountResponse {
type Error = ConversionError;
fn try_from(value: proto::rpc::AccountResponse) -> Result<Self, Self::Error> {
let decoder = value.decoder();
let proto::rpc::AccountResponse { block_num, witness, details } = value;
let block_num = block_num
.ok_or(ConversionError::missing_field::<proto::rpc::AccountResponse>("block_num"))?
.into();
let witness = decode!(decoder, witness)?;
let details = details.map(TryFrom::try_from).transpose().context("details")?;
Ok(AccountResponse { block_num, witness, details })
}
}
impl From<AccountResponse> for proto::rpc::AccountResponse {
fn from(value: AccountResponse) -> Self {
let AccountResponse { block_num, witness, details } = value;
Self {
witness: Some(witness.into()),
details: details.map(Into::into),
block_num: Some(block_num.into()),
}
}
}
pub struct AccountDetails {
pub account_header: AccountHeader,
pub account_code: Option<Vec<u8>>,
pub vault_details: AccountVaultDetails,
pub storage_details: AccountStorageDetails,
}
impl AccountDetails {
pub fn with_storage_limits_exceeded(
account_header: AccountHeader,
account_code: Option<Vec<u8>>,
vault_details: AccountVaultDetails,
storage_header: AccountStorageHeader,
slot_names: impl IntoIterator<Item = StorageSlotName>,
) -> Self {
Self {
account_header,
account_code,
vault_details,
storage_details: AccountStorageDetails::all_limits_exceeded(storage_header, slot_names),
}
}
}
impl TryFrom<proto::rpc::account_response::AccountDetails> for AccountDetails {
type Error = ConversionError;
fn try_from(value: proto::rpc::account_response::AccountDetails) -> Result<Self, Self::Error> {
let decoder = value.decoder();
let proto::rpc::account_response::AccountDetails {
header,
code,
vault_details,
storage_details,
} = value;
let account_header = decode!(decoder, header)?;
let storage_details = decode!(decoder, storage_details)?;
let vault_details = decode!(decoder, vault_details)?;
let account_code = code;
Ok(AccountDetails {
account_header,
account_code,
vault_details,
storage_details,
})
}
}
impl From<AccountDetails> for proto::rpc::account_response::AccountDetails {
fn from(value: AccountDetails) -> Self {
let AccountDetails {
account_header,
storage_details,
account_code,
vault_details,
} = value;
let header = Some(proto::account::AccountHeader::from(account_header));
let storage_details = Some(storage_details.into());
let code = account_code;
let vault_details = Some(vault_details.into());
Self {
header,
storage_details,
code,
vault_details,
}
}
}
impl TryFrom<proto::account::AccountWitness> for AccountWitness {
type Error = ConversionError;
fn try_from(account_witness: proto::account::AccountWitness) -> Result<Self, Self::Error> {
let decoder = account_witness.decoder();
let witness_id = decode!(decoder, account_witness.witness_id)?;
let commitment = decode!(decoder, account_witness.commitment)?;
let path = decode!(decoder, account_witness.path)?;
AccountWitness::new(witness_id, commitment, path).map_err(|err| {
ConversionError::deserialization(
"AccountWitness",
DeserializationError::InvalidValue(err.to_string()),
)
})
}
}
impl From<AccountWitness> for proto::account::AccountWitness {
fn from(witness: AccountWitness) -> Self {
Self {
account_id: Some(witness.id().into()),
witness_id: Some(witness.id().into()),
commitment: Some(witness.state_commitment().into()),
path: Some(witness.into_proof().into_parts().0.into()),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AccountWitnessRecord {
pub account_id: AccountId,
pub witness: AccountWitness,
}
impl TryFrom<proto::account::AccountWitness> for AccountWitnessRecord {
type Error = ConversionError;
fn try_from(
account_witness_record: proto::account::AccountWitness,
) -> Result<Self, Self::Error> {
let decoder = account_witness_record.decoder();
let witness_id = decode!(decoder, account_witness_record.witness_id)?;
let commitment = decode!(decoder, account_witness_record.commitment)?;
let account_id = decode!(decoder, account_witness_record.account_id)?;
let path: SparseMerklePath = decode!(decoder, account_witness_record.path)?;
let witness = AccountWitness::new(witness_id, commitment, path).map_err(|err| {
ConversionError::deserialization(
"AccountWitness",
DeserializationError::InvalidValue(err.to_string()),
)
})?;
Ok(Self { account_id, witness })
}
}
impl From<AccountWitnessRecord> for proto::account::AccountWitness {
fn from(from: AccountWitnessRecord) -> Self {
Self {
account_id: Some(from.account_id.into()),
witness_id: Some(from.witness.id().into()),
commitment: Some(from.witness.state_commitment().into()),
path: Some(from.witness.path().clone().into()),
}
}
}
#[derive(Debug)]
pub struct AccountState {
pub account_id: AccountId,
pub account_commitment: Option<Word>,
}
impl Display for AccountState {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{{ account_id: {}, account_commitment: {} }}",
self.account_id,
format_opt(self.account_commitment.as_ref()),
))
}
}
impl TryFrom<proto::store::transaction_inputs::AccountTransactionInputRecord> for AccountState {
type Error = ConversionError;
fn try_from(
from: proto::store::transaction_inputs::AccountTransactionInputRecord,
) -> Result<Self, Self::Error> {
let decoder = from.decoder();
let account_id = decode!(decoder, from.account_id)?;
let account_commitment = decode!(decoder, from.account_commitment)?;
let account_commitment = if account_commitment == Word::empty() {
None
} else {
Some(account_commitment)
};
Ok(Self { account_id, account_commitment })
}
}
impl From<AccountState> for proto::store::transaction_inputs::AccountTransactionInputRecord {
fn from(from: AccountState) -> Self {
Self {
account_id: Some(from.account_id.into()),
account_commitment: from.account_commitment.map(Into::into),
}
}
}
impl TryFrom<proto::primitives::Asset> for Asset {
type Error = ConversionError;
fn try_from(asset: proto::primitives::Asset) -> Result<Self, Self::Error> {
let decoder = asset.decoder();
let key_word: Word = decode!(decoder, asset.key)?;
let value_word: Word = decode!(decoder, asset.value)?;
let asset = Asset::from_key_value_words(key_word, value_word)?;
Ok(asset)
}
}
impl From<Asset> for proto::primitives::Asset {
fn from(asset_from: Asset) -> Self {
proto::primitives::Asset {
key: Some(asset_from.to_key_word().into()),
value: Some(asset_from.to_value_word().into()),
}
}
}
pub type AccountPrefix = u32;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct NetworkAccountId(AccountId);
impl std::fmt::Display for NetworkAccountId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl NetworkAccountId {
pub fn inner(&self) -> AccountId {
self.0
}
pub fn prefix(&self) -> AccountPrefix {
get_account_id_tag_prefix(self.0)
}
}
impl TryFrom<AccountId> for NetworkAccountId {
type Error = NetworkAccountError;
fn try_from(id: AccountId) -> Result<Self, Self::Error> {
if !id.is_network() {
return Err(NetworkAccountError::NotNetworkAccount(id));
}
Ok(NetworkAccountId(id))
}
}
impl TryFrom<&NoteAttachment> for NetworkAccountId {
type Error = NetworkAccountError;
fn try_from(attachment: &NoteAttachment) -> Result<Self, Self::Error> {
let target = NetworkAccountTarget::try_from(attachment)
.map_err(NetworkAccountError::InvalidAttachment)?;
Ok(NetworkAccountId(target.target_id()))
}
}
impl TryFrom<NoteAttachment> for NetworkAccountId {
type Error = NetworkAccountError;
fn try_from(attachment: NoteAttachment) -> Result<Self, Self::Error> {
NetworkAccountId::try_from(&attachment)
}
}
impl From<NetworkAccountId> for AccountId {
fn from(value: NetworkAccountId) -> Self {
value.inner()
}
}
impl From<NetworkAccountId> for u32 {
fn from(value: NetworkAccountId) -> Self {
value.prefix()
}
}
#[derive(Debug, Error)]
pub enum NetworkAccountError {
#[error("account ID {0} is not a valid network account ID")]
NotNetworkAccount(AccountId),
#[error("invalid network account attachment: {0}")]
InvalidAttachment(#[source] NetworkAccountTargetError),
}
fn get_account_id_tag_prefix(id: AccountId) -> AccountPrefix {
(id.prefix().as_u64() >> 34) as AccountPrefix
}