radix_transactions/model/preparation/
traits.rs

1use super::*;
2use crate::internal_prelude::*;
3use sbor::*;
4
5pub trait TransactionPayload:
6    ManifestEncode
7    + ManifestDecode
8    + ManifestCategorize
9    + for<'a> ManifestSborEnumVariantFor<
10        AnyTransaction,
11        OwnedVariant: ManifestDecode,
12        BorrowedVariant<'a>: ManifestEncode,
13    >
14{
15    type Prepared: PreparedTransaction<Raw = Self::Raw>;
16    type Raw: RawTransactionPayload;
17
18    fn discriminator() -> u8 {
19        Self::DISCRIMINATOR
20    }
21
22    fn to_raw(&self) -> Result<Self::Raw, EncodeError> {
23        Ok(manifest_encode(&self.as_encodable_variant())?.into())
24    }
25
26    fn from_raw(raw: &Self::Raw) -> Result<Self, DecodeError> {
27        Ok(Self::from_decoded_variant(manifest_decode(raw.as_ref())?))
28    }
29
30    fn from_payload_variant(payload_variant: Self::OwnedVariant) -> Self {
31        Self::from_decoded_variant(payload_variant)
32    }
33
34    fn prepare(&self, settings: &PreparationSettings) -> Result<Self::Prepared, PrepareError> {
35        Ok(Self::Prepared::prepare(&self.to_raw()?, settings)?)
36    }
37}
38
39pub trait TransactionPartialPrepare: ManifestEncode {
40    type Prepared: TransactionPreparableFromValue;
41
42    fn prepare_partial(
43        &self,
44        settings: &PreparationSettings,
45    ) -> Result<Self::Prepared, PrepareError> {
46        let payload = manifest_encode(self).unwrap();
47        let mut transaction_decoder = TransactionDecoder::new_partial(&payload, settings)?;
48        let prepared = Self::Prepared::prepare_from_value(&mut transaction_decoder)?;
49        transaction_decoder.check_complete()?;
50        Ok(prepared)
51    }
52}
53
54/// Intended for use when the value is encoded without a prefix byte,
55/// e.g. when it's under an array.
56///
57/// Should only decode the value body, NOT read the SBOR value kind.
58///
59/// NOTE:
60/// * The hash should align with the hash from other means.
61///   Ideally this means the hash should _not_ include the header byte.
62/// * Ideally the summary should not include costing for reading the value kind byte.
63pub trait TransactionPreparableFromValueBody: HasSummary + Sized {
64    /// Most types when read as a value should have a slightly longer length.
65    /// BUT some types (e.g. transaction payloads) must have the same length
66    /// regardless, as this length is used for billing the transaction.
67    const ADDITIONAL_SUMMARY_LENGTH_AS_VALUE: usize = 1usize;
68
69    /// Prepares the transaction from a transaction decoder by reading the inner body
70    /// of the tuple/enum (without the value kind)
71    fn prepare_from_value_body(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError>;
72
73    fn value_kind() -> ManifestValueKind;
74}
75
76impl<T: TransactionPreparableFromValueBody> TransactionPreparableFromValue for T {
77    fn prepare_from_value(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError> {
78        decoder.read_and_check_value_kind(Self::value_kind())?;
79        let mut prepared = Self::prepare_from_value_body(decoder)?;
80        // Add the extra byte to the effective length
81        prepared.summary_mut().effective_length = prepared
82            .get_summary()
83            .effective_length
84            .checked_add(Self::ADDITIONAL_SUMMARY_LENGTH_AS_VALUE)
85            .ok_or(PrepareError::LengthOverflow)?;
86        Ok(prepared)
87    }
88}
89
90/// Should read the SBOR value kind, and then the rest of the SBOR value.
91///
92/// NOTE:
93/// * In V1, the hash included the value kind byte.
94/// * In V2, the hash does _not_ include the value kind byte (which enables the
95///   hash to be the same when a Vec child as well as when full values).
96///
97/// There is a blanket implementation of `TransactionPreparableFromValue` for types
98/// which support `TransactionPreparableFromValueBody`, therefore from V2 onwards,
99/// this should not be implemented directly.
100pub trait TransactionPreparableFromValue: HasSummary + Sized {
101    /// Prepares value from a manifest decoder by reading the full SBOR value
102    /// That is - the value kind, and then the value body.
103    fn prepare_from_value(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError>;
104}
105
106/// Transaction payloads are models which are intended to be passed around in their raw form.
107///
108/// They should have a hash over a payload which starts with the bytes:
109/// * `TRANSACTION_HASHABLE_PAYLOAD_PREFIX`
110/// * `TransactionDiscriminator::X as u8`
111pub trait PreparedTransaction: Sized {
112    type Raw: RawTransactionPayload;
113
114    /// Prepares value from a transaction decoder by reading the Enum wrapper
115    /// (including its value kind)
116    fn prepare_from_transaction_enum(
117        decoder: &mut TransactionDecoder,
118    ) -> Result<Self, PrepareError>;
119
120    fn prepare(raw: &Self::Raw, settings: &PreparationSettings) -> Result<Self, PrepareError> {
121        let payload = raw.as_slice();
122        let mut transaction_decoder = TransactionDecoder::new_transaction(
123            payload,
124            <Self::Raw as RawTransactionPayload>::KIND,
125            settings,
126        )?;
127        let prepared = Self::prepare_from_transaction_enum(&mut transaction_decoder)?;
128        transaction_decoder.check_complete()?;
129        Ok(prepared)
130    }
131}
132
133macro_rules! define_transaction_payload {
134    (
135        $transaction:ident,
136        $raw:ty,
137        $prepared:ident {
138            $($field_name:ident: $field_type:ty,)*
139        },
140        $discriminator:expr,
141    ) => {
142        #[derive(Debug, Clone, Eq, PartialEq)]
143        pub struct $prepared {
144            $(pub $field_name: $field_type,)*
145            pub summary: Summary,
146        }
147
148        impl TransactionPayload for $transaction {
149            type Prepared = $prepared;
150            type Raw = $raw;
151        }
152
153        impl HasSummary for $prepared {
154            fn get_summary(&self) -> &Summary {
155                &self.summary
156            }
157
158            fn summary_mut(&mut self) -> &mut Summary {
159                &mut self.summary
160            }
161        }
162
163        impl PreparedTransaction for $prepared {
164            type Raw = $raw;
165
166            fn prepare_from_transaction_enum(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError> {
167                // When embedded as full payload, it's SBOR encoded as an enum
168                let (($($field_name,)*), summary) = ConcatenatedDigest::prepare_transaction_payload(
169                    decoder,
170                    $discriminator,
171                    ExpectedHeaderKind::EnumWithValueKind,
172                )?;
173                Ok(Self {
174                    $($field_name,)*
175                    summary,
176                })
177            }
178        }
179
180        impl TransactionPreparableFromValueBody for $prepared {
181            // Ensure that all manners of preparing the transaction give an
182            // equal effective length, so they can all be turned into an executable
183            // and have the same billed length.
184            const ADDITIONAL_SUMMARY_LENGTH_AS_VALUE: usize = 0;
185
186            fn prepare_from_value_body(decoder: &mut TransactionDecoder) -> Result<Self, PrepareError> {
187                // When embedded as an child body, it's SBOR encoded as a struct body (without value kind)
188                let (($($field_name,)*), summary) =
189                    ConcatenatedDigest::prepare_transaction_payload(
190                        decoder,
191                        $discriminator,
192                        ExpectedHeaderKind::TupleNoValueKind,
193                    )?;
194                Ok(Self {
195                    $($field_name,)*
196                    summary,
197                })
198            }
199
200            fn value_kind() -> ManifestValueKind {
201                ManifestValueKind::Tuple
202            }
203        }
204    };
205}
206
207pub(crate) use define_transaction_payload;
208
209pub trait RawTransactionPayload: AsRef<[u8]> + From<Vec<u8>> + Into<Vec<u8>> {
210    const KIND: TransactionPayloadKind;
211
212    fn as_slice(&self) -> &[u8] {
213        self.as_ref()
214    }
215}
216
217pub trait ValidatedTransactionPayload: IntoExecutable {}
218
219#[derive(Debug, Copy, Clone)]
220pub enum TransactionPayloadKind {
221    CompleteUserTransaction,
222    LedgerTransaction,
223    Other,
224}
225
226#[macro_export]
227macro_rules! define_raw_transaction_payload {
228    ($(#[$docs:meta])* $name:ident, $kind:expr) => {
229        #[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Sbor)]
230        #[sbor(transparent)]
231        $(#[$docs])*
232        pub struct $name(Vec<u8>);
233
234        impl AsRef<[u8]> for $name {
235            fn as_ref(&self) -> &[u8] {
236                self.0.as_ref()
237            }
238        }
239
240        impl From<Vec<u8>> for $name {
241            fn from(value: Vec<u8>) -> Self {
242                Self(value)
243            }
244        }
245
246        impl From<$name> for Vec<u8> {
247            fn from(value: $name) -> Self {
248                value.0
249            }
250        }
251
252        impl RawTransactionPayload for $name {
253            const KIND: TransactionPayloadKind = $kind;
254        }
255
256        impl $name {
257            pub fn as_slice(&self) -> &[u8] {
258                self.0.as_slice()
259            }
260
261            pub fn len(&self) -> usize {
262                self.as_slice().len()
263            }
264
265            pub fn to_vec(self) -> Vec<u8> {
266                self.0
267            }
268
269            pub fn from_vec(vec: Vec<u8>) -> Self {
270                Self(vec)
271            }
272
273            pub fn from_slice(slice: impl AsRef<[u8]>) -> Self {
274                Self(slice.as_ref().into())
275            }
276
277            pub fn to_hex(&self) -> String {
278                hex::encode(self.as_slice())
279            }
280
281            pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, hex::FromHexError> {
282                Ok(Self(hex::decode(hex)?))
283            }
284        }
285    };
286}