radix_transactions/manifest/
any_manifest.rs

1use crate::internal_prelude::*;
2
3/// This is the new compile/decompile target for saved transaction manifests,
4/// and generally a type to support an unknown kind of manifest at runtime.
5///
6/// ## Using AnyManifest
7/// Sometimes a method can take &impl ReadableManifest, which is preferred if possible.
8///
9/// Sometimes however a particular type is required for a method, needing an impl [`TypedReadableManifest`].
10/// In which case, we can add a `XXX_any(...)` method which takes an [`AnyManifest`]
11/// and then uses a `match` statement to delegate to the correct typed method.
12///
13/// Ideally, we could have an apply method which lets you use this method trivially with
14/// an [`AnyManifest`] - but this would require a function constraint of
15/// `F: for<R: ReadableManifest> FnOnce<R, Output>` - which uses higher order type-based trait bounds
16/// which don't exist yet (https://github.com/rust-lang/rust/issues/108185).
17#[derive(Debug, Clone, PartialEq, Eq, ManifestSbor, ScryptoDescribe)]
18pub enum AnyManifest {
19    V1(TransactionManifestV1),
20    SystemV1(SystemTransactionManifestV1),
21    V2(TransactionManifestV2),
22    SubintentV2(SubintentManifestV2),
23}
24
25impl From<TransactionManifestV1> for AnyManifest {
26    fn from(value: TransactionManifestV1) -> Self {
27        Self::V1(value)
28    }
29}
30
31impl TryFrom<AnyManifest> for TransactionManifestV1 {
32    type Error = ();
33
34    fn try_from(value: AnyManifest) -> Result<Self, Self::Error> {
35        match value {
36            AnyManifest::V1(manifest) => Ok(manifest),
37            _ => Err(()),
38        }
39    }
40}
41
42impl From<SystemTransactionManifestV1> for AnyManifest {
43    fn from(value: SystemTransactionManifestV1) -> Self {
44        Self::SystemV1(value)
45    }
46}
47
48impl TryFrom<AnyManifest> for SystemTransactionManifestV1 {
49    type Error = ();
50
51    fn try_from(value: AnyManifest) -> Result<Self, Self::Error> {
52        match value {
53            AnyManifest::SystemV1(manifest) => Ok(manifest),
54            _ => Err(()),
55        }
56    }
57}
58
59impl From<TransactionManifestV2> for AnyManifest {
60    fn from(value: TransactionManifestV2) -> Self {
61        Self::V2(value)
62    }
63}
64
65impl TryFrom<AnyManifest> for TransactionManifestV2 {
66    type Error = ();
67
68    fn try_from(value: AnyManifest) -> Result<Self, Self::Error> {
69        match value {
70            AnyManifest::V2(manifest) => Ok(manifest),
71            _ => Err(()),
72        }
73    }
74}
75
76impl From<SubintentManifestV2> for AnyManifest {
77    fn from(value: SubintentManifestV2) -> Self {
78        Self::SubintentV2(value)
79    }
80}
81
82impl TryFrom<AnyManifest> for SubintentManifestV2 {
83    type Error = ();
84
85    fn try_from(value: AnyManifest) -> Result<Self, Self::Error> {
86        match value {
87            AnyManifest::SubintentV2(manifest) => Ok(manifest),
88            _ => Err(()),
89        }
90    }
91}
92
93// It's not technically a conventional transaction payload, but let's reuse the macro
94define_raw_transaction_payload!(RawManifest, TransactionPayloadKind::Other);
95
96impl AnyManifest {
97    pub fn to_raw(&self) -> Result<RawManifest, EncodeError> {
98        Ok(RawManifest::from_vec(manifest_encode(self)?))
99    }
100
101    pub fn from_raw(raw: &RawManifest) -> Result<Self, DecodeError> {
102        Ok(manifest_decode(raw.as_slice())?)
103    }
104
105    pub fn attempt_decode_from_arbitrary_payload(bytes: &[u8]) -> Result<Self, String> {
106        // First, try to decode as AnyManifest
107        if let Ok(any_manifest) = manifest_decode::<Self>(bytes) {
108            return Ok(any_manifest);
109        }
110
111        // If that fails, try LegacyTransactionManifestV1
112        if let Ok(legacy_v1_manifest) = manifest_decode::<LegacyTransactionManifestV1>(bytes) {
113            return Ok(Self::V1(legacy_v1_manifest.into()));
114        }
115
116        // Finally, try as AnyTransaction
117        if let Ok(any_transaction) = manifest_decode::<AnyTransaction>(bytes) {
118            return Ok(match any_transaction {
119                AnyTransaction::TransactionIntentV1(intent) => {
120                    TransactionManifestV1::from_intent(&intent).into()
121                }
122                AnyTransaction::SignedTransactionIntentV1(signed_intent) => {
123                    TransactionManifestV1::from_intent(&signed_intent.intent).into()
124                }
125                AnyTransaction::NotarizedTransactionV1(notarized) => {
126                    TransactionManifestV1::from_intent(&notarized.signed_intent.intent).into()
127                }
128                AnyTransaction::SystemTransactionV1(system_transaction) => {
129                    SystemTransactionManifestV1::from_transaction(&system_transaction).into()
130                }
131                AnyTransaction::TransactionIntentV2(intent) => {
132                    TransactionManifestV2::from_intent_core(&intent.root_intent_core).into()
133                }
134                AnyTransaction::SignedTransactionIntentV2(signed_intent) => {
135                    TransactionManifestV2::from_intent_core(
136                        &signed_intent.transaction_intent.root_intent_core,
137                    )
138                    .into()
139                }
140                AnyTransaction::NotarizedTransactionV2(notarized) => {
141                    TransactionManifestV2::from_intent_core(
142                        &notarized
143                            .signed_transaction_intent
144                            .transaction_intent
145                            .root_intent_core,
146                    )
147                    .into()
148                }
149                AnyTransaction::SubintentV2(subintent) => {
150                    SubintentManifestV2::from_intent_core(&subintent.intent_core).into()
151                }
152                other_type => {
153                    return Err(format!(
154                        "Transaction type with discriminator {} not currently supported",
155                        other_type.get_discriminator()
156                    ))
157                }
158            });
159        }
160
161        Err(format!(
162            "Cannot decode transaction manifest or transaction payload"
163        ))
164    }
165}
166
167impl ReadableManifestBase for AnyManifest {
168    fn is_subintent(&self) -> bool {
169        match self {
170            AnyManifest::V1(m) => m.is_subintent(),
171            AnyManifest::SystemV1(m) => m.is_subintent(),
172            AnyManifest::V2(m) => m.is_subintent(),
173            AnyManifest::SubintentV2(m) => m.is_subintent(),
174        }
175    }
176
177    fn get_blobs<'a>(&'a self) -> impl Iterator<Item = (&'a Hash, &'a Vec<u8>)> {
178        let iterator: Box<dyn Iterator<Item = (&'a Hash, &'a Vec<u8>)> + 'a> = match self {
179            AnyManifest::V1(m) => Box::new(m.get_blobs()),
180            AnyManifest::SystemV1(m) => Box::new(m.get_blobs()),
181            AnyManifest::V2(m) => Box::new(m.get_blobs()),
182            AnyManifest::SubintentV2(m) => Box::new(m.get_blobs()),
183        };
184        iterator
185    }
186
187    fn get_known_object_names_ref(&self) -> ManifestObjectNamesRef {
188        match self {
189            AnyManifest::V1(m) => m.get_known_object_names_ref(),
190            AnyManifest::SystemV1(m) => m.get_known_object_names_ref(),
191            AnyManifest::V2(m) => m.get_known_object_names_ref(),
192            AnyManifest::SubintentV2(m) => m.get_known_object_names_ref(),
193        }
194    }
195
196    fn get_preallocated_addresses(&self) -> &[PreAllocatedAddress] {
197        match self {
198            AnyManifest::V1(m) => m.get_preallocated_addresses(),
199            AnyManifest::SystemV1(m) => m.get_preallocated_addresses(),
200            AnyManifest::V2(m) => m.get_preallocated_addresses(),
201            AnyManifest::SubintentV2(m) => m.get_preallocated_addresses(),
202        }
203    }
204
205    fn get_child_subintent_hashes<'a>(
206        &'a self,
207    ) -> impl ExactSizeIterator<Item = &'a ChildSubintentSpecifier> {
208        let iterator: Box<dyn ExactSizeIterator<Item = &'a ChildSubintentSpecifier>> = match self {
209            AnyManifest::V1(m) => Box::new(m.get_child_subintent_hashes()),
210            AnyManifest::SystemV1(m) => Box::new(m.get_child_subintent_hashes()),
211            AnyManifest::V2(m) => Box::new(m.get_child_subintent_hashes()),
212            AnyManifest::SubintentV2(m) => Box::new(m.get_child_subintent_hashes()),
213        };
214        iterator
215    }
216}
217
218impl ReadableManifest for AnyManifest {
219    fn iter_instruction_effects(&self) -> impl Iterator<Item = ManifestInstructionEffect> {
220        let iterator: Box<dyn Iterator<Item = ManifestInstructionEffect>> = match self {
221            AnyManifest::V1(m) => Box::new(m.iter_instruction_effects()),
222            AnyManifest::SystemV1(m) => Box::new(m.iter_instruction_effects()),
223            AnyManifest::V2(m) => Box::new(m.iter_instruction_effects()),
224            AnyManifest::SubintentV2(m) => Box::new(m.iter_instruction_effects()),
225        };
226        iterator
227    }
228
229    fn iter_cloned_instructions(&self) -> impl Iterator<Item = AnyInstruction> {
230        let iterator: Box<dyn Iterator<Item = AnyInstruction>> = match self {
231            AnyManifest::V1(m) => Box::new(m.iter_cloned_instructions()),
232            AnyManifest::SystemV1(m) => Box::new(m.iter_cloned_instructions()),
233            AnyManifest::V2(m) => Box::new(m.iter_cloned_instructions()),
234            AnyManifest::SubintentV2(m) => Box::new(m.iter_cloned_instructions()),
235        };
236        iterator
237    }
238
239    fn instruction_count(&self) -> usize {
240        match self {
241            AnyManifest::V1(m) => m.instruction_count(),
242            AnyManifest::SystemV1(m) => m.instruction_count(),
243            AnyManifest::V2(m) => m.instruction_count(),
244            AnyManifest::SubintentV2(m) => m.instruction_count(),
245        }
246    }
247
248    fn instruction_effect(&self, index: usize) -> ManifestInstructionEffect {
249        match self {
250            AnyManifest::V1(m) => m.instruction_effect(index),
251            AnyManifest::SystemV1(m) => m.instruction_effect(index),
252            AnyManifest::V2(m) => m.instruction_effect(index),
253            AnyManifest::SubintentV2(m) => m.instruction_effect(index),
254        }
255    }
256}
257
258pub enum ManifestKind {
259    V1,
260    SystemV1,
261    V2,
262    SubintentV2,
263}
264
265impl ManifestKind {
266    const LATEST_SYSTEM: Self = Self::SystemV1;
267    const LATEST_TRANSACTION: Self = Self::V2;
268    const LATEST_SUBINTENT: Self = Self::SubintentV2;
269
270    pub fn parse_or_latest(arg: Option<&str>) -> Result<Self, String> {
271        match arg {
272            Some(kind) => kind.try_into(),
273            None => Ok(Self::LATEST_TRANSACTION),
274        }
275    }
276}
277
278impl TryFrom<&str> for ManifestKind {
279    type Error = String;
280
281    fn try_from(value: &str) -> Result<Self, Self::Error> {
282        let kind = match value.to_ascii_lowercase().as_str() {
283            "v1" => Self::V1,
284            "systemv1" => Self::SystemV1,
285            "v2" => Self::V2,
286            "subintentv2" => Self::SubintentV2,
287            "system" => Self::LATEST_SYSTEM,
288            "transaction" => Self::LATEST_TRANSACTION,
289            "subintent" => Self::LATEST_SUBINTENT,
290            _ => {
291                return Err(format!(
292                    "Manifest kind not recognized. Try one of: V1 | SystemV1 | V2 | SubintentV2"
293                ))
294            }
295        };
296        Ok(kind)
297    }
298}