use std::fmt::{Debug, Display, Formatter};
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;
use miden_protocol::block::BlockNumber;
use miden_protocol::block::account_tree::AccountWitness;
use miden_protocol::crypto::merkle::smt::{PartialSmt, SmtProof};
use miden_protocol::crypto::merkle::{MerkleError, SparseMerklePath};
use miden_protocol::utils::serde::{Deserializable, DeserializationError, Serializable};
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)?)
}
}
#[derive(Debug)]
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 })
}
}
#[derive(Debug)]
pub struct AccountDetailRequest {
pub code_commitment: Option<Word>,
pub asset_vault_commitment: Option<Word>,
pub storage_request: AccountStorageRequest,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AccountStorageRequest {
None,
AllStorageMaps,
Explicit(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> {
use proto::rpc::account_request::account_detail_request::StorageRequest as ProtoStorageRequest;
let proto::rpc::account_request::AccountDetailRequest {
code_commitment,
asset_vault_commitment,
storage_request,
} = 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_request = match storage_request {
None => AccountStorageRequest::None,
Some(ProtoStorageRequest::AllStorageMaps(true)) => {
AccountStorageRequest::AllStorageMaps
},
Some(ProtoStorageRequest::AllStorageMaps(false)) => {
return Err(ConversionError::message("all_storage_maps must be true when set"));
},
Some(ProtoStorageRequest::StorageMaps(requests)) => {
let requests = try_convert(requests.storage_maps)
.collect::<Result<_, _>>()
.context("storage_maps")?;
AccountStorageRequest::Explicit(requests)
},
};
Ok(AccountDetailRequest {
code_commitment,
asset_vault_commitment,
storage_request,
})
}
}
#[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<_>, _>>()?;
if has_duplicate_storage_map_keys(&keys) {
return Err(ConversionError::message(
"storage map key request contains duplicate keys",
));
}
SlotData::MapKeys(keys)
},
})
}
}
fn has_duplicate_storage_map_keys(keys: &[StorageMapKey]) -> bool {
keys.iter().enumerate().any(|(index, key)| keys[..index].contains(key))
}
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 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 = assets
.into_iter()
.map(Asset::try_from)
.collect::<Result<Vec<_>, ConversionError>>()?;
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: assets.into_iter().map(proto::primitives::Asset::from).collect::<Vec<_>>(),
},
}
}
}
#[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)>),
PartialMap {
map_keys: Vec<StorageMapKey>,
partial_smt: PartialSmt,
},
}
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 = storage_map.entries().map(|(k, v)| (*k, *v)).collect::<Vec<_>>();
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,
map_root: Word,
map_keys: Vec<StorageMapKey>,
proofs: Vec<SmtProof>,
) -> Result<Self, MerkleError> {
if map_keys.len() != proofs.len() {
return Err(MerkleError::InternalError(format!(
"storage map key count {} does not match proof count {}",
map_keys.len(),
proofs.len()
)));
}
if has_duplicate_storage_map_keys(&map_keys) {
return Err(MerkleError::InternalError(
"storage map key list contains duplicate keys".into(),
));
}
if map_keys.len() > Self::MAX_SMT_PROOF_ENTRIES {
return Ok(Self {
slot_name,
entries: StorageMapEntries::LimitExceeded,
});
}
let partial_smt = if proofs.is_empty() {
PartialSmt::new(map_root)
} else {
PartialSmt::from_proofs(proofs)?
};
if partial_smt.root() != map_root {
return Err(MerkleError::ConflictingRoots {
expected_root: map_root,
actual_root: partial_smt.root(),
});
}
for map_key in &map_keys {
partial_smt.get_value(&map_key.hash().as_word())?;
}
Ok(Self {
slot_name,
entries: StorageMapEntries::PartialMap { map_keys, partial_smt },
})
}
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,
PartialStorageMap,
Result as ProtoResult,
};
let decoder = value.decoder();
let proto::rpc::account_storage_details::AccountStorageMapDetails { slot_name, result } =
value;
let slot_name = StorageSlotName::new(slot_name).context("slot_name")?;
let entries = match decode!(decoder, result)? {
ProtoResult::TooManyEntries(true) => StorageMapEntries::LimitExceeded,
ProtoResult::TooManyEntries(false) => {
return Err(ConversionError::message("too_many_entries must be true when set"));
},
ProtoResult::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)
},
ProtoResult::PartialMap(PartialStorageMap { map_keys, partial_smt }) => {
if map_keys.len() > Self::MAX_SMT_PROOF_ENTRIES {
return Err(ConversionError::message(format!(
"partial storage map contains {} keys, exceeding the limit of {}",
map_keys.len(),
Self::MAX_SMT_PROOF_ENTRIES
)));
}
let map_keys = map_keys
.into_iter()
.map(|key| Word::try_from(key).map(StorageMapKey::new))
.collect::<Result<Vec<_>, _>>()
.context("map_keys")?;
if has_duplicate_storage_map_keys(&map_keys) {
return Err(ConversionError::message(
"partial storage map contains duplicate keys",
));
}
let partial_smt: PartialSmt =
decode!(decoder, partial_smt).context("partial_smt")?;
for map_key in &map_keys {
partial_smt.get_value(&map_key.hash().as_word()).context("map_keys")?;
}
StorageMapEntries::PartialMap { map_keys, partial_smt }
},
};
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,
PartialStorageMap,
Result as ProtoResult,
};
let AccountStorageMapDetails { slot_name, entries } = value;
let result = match entries {
StorageMapEntries::LimitExceeded => ProtoResult::TooManyEntries(true),
StorageMapEntries::AllEntries(entries) => {
let all = AllMapEntries {
entries: 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()),
}
}).collect::<Vec<_>>(),
};
ProtoResult::AllEntries(all)
},
StorageMapEntries::PartialMap { map_keys, partial_smt } => {
ProtoResult::PartialMap(PartialStorageMap {
map_keys: map_keys.into_iter().map(Into::into).collect(),
partial_smt: Some(partial_smt.into()),
})
},
};
Self {
slot_name: slot_name.to_string(),
result: Some(result),
}
}
}
#[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: slot_names
.into_iter()
.map(AccountStorageMapDetails::limit_exceeded)
.collect::<Vec<_>>(),
}
}
}
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: AccountStorageHeader = decode!(decoder, header)?;
let map_details: Vec<AccountStorageMapDetails> =
try_convert(map_details).collect::<Result<Vec<_>, _>>().context("map_details")?;
for map_detail in &map_details {
let StorageMapEntries::PartialMap { partial_smt, .. } = &map_detail.entries else {
continue;
};
let slot = header.find_slot_header_by_name(&map_detail.slot_name).ok_or_else(|| {
ConversionError::message(format!(
"partial storage map references unknown slot {}",
map_detail.slot_name
))
})?;
if slot.slot_type() != StorageSlotType::Map {
return Err(ConversionError::message(format!(
"partial storage map references non-map slot {}",
map_detail.slot_name
)));
}
if partial_smt.root() != slot.value() {
return Err(ConversionError::message(format!(
"partial storage map root for slot {} does not match storage header",
map_detail.slot_name
)));
}
}
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 = decode!(decoder, block_num)?;
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()),
}
}
}
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_id_and_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_id_word().into()),
value: Some(asset_from.to_value_word().into()),
}
}
}