use std::string::ToString;
use std::vec::Vec;
use miden_client::Word;
use miden_client::transaction::{DiscardCause, TransactionDetails, TransactionStatus};
use miden_objects::DecodeMessageExt;
use crate::proto::{self, ProtoDecodeError, ProtobufValue, required};
impl ProtobufValue for TransactionDetails {
type Message = proto::TransactionDetails;
fn to_proto(&self) -> Self::Message {
Self::Message {
account_id: Some(self.account_id.into()),
init_account_state: Some(self.init_account_state.into()),
final_account_state: Some(self.final_account_state.into()),
input_note_nullifiers: self
.input_note_nullifiers
.iter()
.map(|nullifier| (*nullifier).into())
.collect(),
output_notes: Some((&self.output_notes).into()),
block_num: Some(self.block_num.into()),
submission_height: Some(self.submission_height.into()),
expiration_block_num: Some(self.expiration_block_num.into()),
creation_timestamp: self.creation_timestamp,
}
}
fn from_proto(details: Self::Message) -> Result<Self, ProtoDecodeError> {
const MESSAGE: &str = "transaction details";
let input_note_nullifiers = details
.input_note_nullifiers
.into_iter()
.map(Word::try_from)
.collect::<Result<Vec<Word>, _>>()?;
Ok(Self {
account_id: required(details.account_id, MESSAGE, "account id")?.decode_and_verify()?,
init_account_state: required(
details.init_account_state,
MESSAGE,
"initial account state",
)?
.try_into()?,
final_account_state: required(
details.final_account_state,
MESSAGE,
"final account state",
)?
.try_into()?,
input_note_nullifiers,
output_notes: required(details.output_notes, MESSAGE, "output notes")?
.decode_and_verify()?,
block_num: required(details.block_num, MESSAGE, "block number")?.decode_and_verify()?,
submission_height: required(details.submission_height, MESSAGE, "submission height")?
.decode_and_verify()?,
expiration_block_num: required(
details.expiration_block_num,
MESSAGE,
"expiration block number",
)?
.decode_and_verify()?,
creation_timestamp: details.creation_timestamp,
})
}
}
impl ProtobufValue for TransactionStatus {
type Message = proto::TransactionStatus;
fn to_proto(&self) -> Self::Message {
use proto::transaction_status::{Committed, Discarded, Pending, Status};
let status = match self {
TransactionStatus::Pending => Status::Pending(Pending {}),
TransactionStatus::Committed { block_number, commit_timestamp } => {
Status::Committed(Committed {
block_number: Some((*block_number).into()),
commit_timestamp: *commit_timestamp,
})
},
TransactionStatus::Discarded(cause) => Status::Discarded(Discarded {
cause: proto::DiscardCause::from(*cause).into(),
}),
};
Self::Message { status: Some(status) }
}
fn from_proto(status: Self::Message) -> Result<Self, ProtoDecodeError> {
use proto::transaction_status::Status;
const MESSAGE: &str = "transaction status";
match required(status.status, MESSAGE, "variant")? {
Status::Pending(_) => Ok(TransactionStatus::Pending),
Status::Committed(committed) => Ok(TransactionStatus::Committed {
block_number: required(committed.block_number, MESSAGE, "block number")?
.decode_and_verify()?,
commit_timestamp: committed.commit_timestamp,
}),
Status::Discarded(discarded) => Ok(TransactionStatus::Discarded(
proto::DiscardCause::try_from(discarded.cause)?.try_into()?,
)),
}
}
}
impl From<DiscardCause> for proto::DiscardCause {
fn from(cause: DiscardCause) -> Self {
match cause {
DiscardCause::Expired => Self::Expired,
DiscardCause::InputConsumed => Self::InputConsumed,
DiscardCause::DiscardedInitialState => Self::DiscardedInitialState,
DiscardCause::Stale => Self::Stale,
DiscardCause::Superseded => Self::Superseded,
}
}
}
impl TryFrom<proto::DiscardCause> for DiscardCause {
type Error = ProtoDecodeError;
fn try_from(cause: proto::DiscardCause) -> Result<Self, Self::Error> {
match cause {
proto::DiscardCause::Expired => Ok(Self::Expired),
proto::DiscardCause::InputConsumed => Ok(Self::InputConsumed),
proto::DiscardCause::DiscardedInitialState => Ok(Self::DiscardedInitialState),
proto::DiscardCause::Stale => Ok(Self::Stale),
proto::DiscardCause::Superseded => Ok(Self::Superseded),
proto::DiscardCause::Unspecified => {
Err(ProtoDecodeError::InvalidValue("discard cause is unspecified".to_string()))
},
}
}
}