use super::*;
use crate::internal_prelude::*;
use sbor::*;
pub trait TransactionPayload:
ManifestEncode
+ ManifestDecode
+ ManifestCategorize
+ for<'a> ManifestSborEnumVariantFor<
AnyTransaction,
OwnedVariant: ManifestDecode,
BorrowedVariant<'a>: ManifestEncode,
>
{
type Prepared: PreparedTransaction<Raw = Self::Raw>;
type Raw: RawTransactionPayload;
fn discriminator() -> u8 {
Self::DISCRIMINATOR
}
fn to_raw(&self) -> Result<Self::Raw, EncodeError> {
Ok(manifest_encode(&self.as_encodable_variant())?.into())
}
fn from_raw(raw: &Self::Raw) -> Result<Self, DecodeError> {
Ok(Self::from_decoded_variant(manifest_decode(raw.as_ref())?))
}
fn from_payload_variant(payload_variant: Self::OwnedVariant) -> Self {
Self::from_decoded_variant(payload_variant)
}
fn prepare(&self, settings: &PreparationSettings) -> Result<Self::Prepared, PrepareError> {
Self::Prepared::prepare(&self.to_raw()?, settings)
}
}
pub trait TransactionPartialPrepare: ManifestEncode {
type Prepared: TransactionPreparableFromValue;
fn prepare_partial(
&self,
settings: &PreparationSettings,
) -> Result<Self::Prepared, PrepareError> {
let payload = manifest_encode(self).unwrap();
let mut transaction_decoder = TransactionDecoder::new_partial(&payload, settings)?;
let prepared = Self::Prepared::prepare_from_value(&mut transaction_decoder)?;
transaction_decoder.check_complete()?;
Ok(prepared)
}
}
pub trait TransactionPreparableFromValueBody: HasSummary + Sized {
const ADDITIONAL_SUMMARY_LENGTH_AS_VALUE: usize = 1usize;
fn prepare_from_value_body(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError>;
fn value_kind() -> ManifestValueKind;
}
impl<T: TransactionPreparableFromValueBody> TransactionPreparableFromValue for T {
fn prepare_from_value(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError> {
decoder.read_and_check_value_kind(Self::value_kind())?;
let mut prepared = Self::prepare_from_value_body(decoder)?;
prepared.summary_mut().effective_length = prepared
.get_summary()
.effective_length
.checked_add(Self::ADDITIONAL_SUMMARY_LENGTH_AS_VALUE)
.ok_or(PrepareError::LengthOverflow)?;
Ok(prepared)
}
}
pub trait TransactionPreparableFromValue: HasSummary + Sized {
fn prepare_from_value(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError>;
}
pub trait PreparedTransaction: Sized {
type Raw: RawTransactionPayload;
fn prepare_from_transaction_enum(
decoder: &mut TransactionDecoder,
) -> Result<Self, PrepareError>;
fn prepare(raw: &Self::Raw, settings: &PreparationSettings) -> Result<Self, PrepareError> {
let payload = raw.as_slice();
let mut transaction_decoder = TransactionDecoder::new_transaction(
payload,
<Self::Raw as RawTransactionPayload>::KIND,
settings,
)?;
let prepared = Self::prepare_from_transaction_enum(&mut transaction_decoder)?;
transaction_decoder.check_complete()?;
Ok(prepared)
}
}
macro_rules! define_transaction_payload {
(
$transaction:ident,
$raw:ty,
$prepared:ident {
$($field_name:ident: $field_type:ty,)*
},
$discriminator:expr,
) => {
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct $prepared {
$(pub $field_name: $field_type,)*
pub summary: Summary,
}
impl TransactionPayload for $transaction {
type Prepared = $prepared;
type Raw = $raw;
}
impl HasSummary for $prepared {
fn get_summary(&self) -> &Summary {
&self.summary
}
fn summary_mut(&mut self) -> &mut Summary {
&mut self.summary
}
}
impl PreparedTransaction for $prepared {
type Raw = $raw;
fn prepare_from_transaction_enum(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError> {
let (($($field_name,)*), summary) = ConcatenatedDigest::prepare_transaction_payload(
decoder,
$discriminator,
ExpectedHeaderKind::EnumWithValueKind,
)?;
Ok(Self {
$($field_name,)*
summary,
})
}
}
impl TransactionPreparableFromValueBody for $prepared {
const ADDITIONAL_SUMMARY_LENGTH_AS_VALUE: usize = 0;
fn prepare_from_value_body(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError> {
let (($($field_name,)*), summary) =
ConcatenatedDigest::prepare_transaction_payload(
decoder,
$discriminator,
ExpectedHeaderKind::TupleNoValueKind,
)?;
Ok(Self {
$($field_name,)*
summary,
})
}
fn value_kind() -> ManifestValueKind {
ManifestValueKind::Tuple
}
}
};
}
pub(crate) use define_transaction_payload;
pub trait RawTransactionPayload: AsRef<[u8]> + From<Vec<u8>> + Into<Vec<u8>> {
const KIND: TransactionPayloadKind;
fn as_slice(&self) -> &[u8] {
self.as_ref()
}
}
pub trait ValidatedTransactionPayload: IntoExecutable {}
#[derive(Debug, Copy, Clone)]
pub enum TransactionPayloadKind {
CompleteUserTransaction,
LedgerTransaction,
Other,
}
#[macro_export]
macro_rules! define_raw_transaction_payload {
($(#[$docs:meta])* $name:ident, $kind:expr) => {
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Sbor)]
#[sbor(transparent)]
$(#[$docs])*
pub struct $name(Vec<u8>);
impl AsRef<[u8]> for $name {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl From<Vec<u8>> for $name {
fn from(value: Vec<u8>) -> Self {
Self(value)
}
}
impl From<$name> for Vec<u8> {
fn from(value: $name) -> Self {
value.0
}
}
impl RawTransactionPayload for $name {
const KIND: TransactionPayloadKind = $kind;
}
impl $name {
pub fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.as_slice().len()
}
#[allow(clippy::wrong_self_convention)]
pub fn to_vec(self) -> Vec<u8> {
self.0
}
pub fn from_vec(vec: Vec<u8>) -> Self {
Self(vec)
}
pub fn from_slice(slice: impl AsRef<[u8]>) -> Self {
Self(slice.as_ref().into())
}
pub fn to_hex(&self) -> String {
hex::encode(self.as_slice())
}
pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, hex::FromHexError> {
Ok(Self(hex::decode(hex)?))
}
}
};
}