radix_transactions/model/
any_transaction.rs

1use crate::internal_prelude::*;
2
3//=============================================================================
4// TRANSACTION PAYLOAD VERSIONING
5//
6// This file aligns with REP-82 - please see the REP for details on why the
7// payloads are versioned this way.
8//=============================================================================
9
10/// Note - some of these are reserved for use in the node.
11#[derive(Copy, Clone, Debug, Eq, PartialEq, FromRepr)]
12#[repr(u8)]
13pub enum TransactionDiscriminator {
14    V1Intent = V1_INTENT,
15    V1SignedIntent = V1_SIGNED_INTENT,
16    V1Notarized = V1_NOTARIZED_TRANSACTION,
17    V1System = V1_SYSTEM_TRANSACTION,
18    V1RoundUpdate = V1_ROUND_UPDATE_TRANSACTION,
19    Ledger = LEDGER_TRANSACTION,
20    V1Flash = V1_FLASH_TRANSACTION,
21    V2TransactionIntent = V2_TRANSACTION_INTENT,
22    V2SignedTransactionIntent = V2_SIGNED_TRANSACTION_INTENT,
23    V2Subintent = V2_SUBINTENT,
24    V2Notarized = V2_NOTARIZED_TRANSACTION,
25    V2PartialTransaction = V2_PARTIAL_TRANSACTION,
26    V2SignedPartialTransaction = V2_SIGNED_PARTIAL_TRANSACTION,
27    V2PreviewTransaction = V2_PREVIEW_TRANSACTION,
28}
29
30const V1_INTENT: u8 = 1;
31const V1_SIGNED_INTENT: u8 = 2;
32const V1_NOTARIZED_TRANSACTION: u8 = 3;
33const V1_SYSTEM_TRANSACTION: u8 = 4;
34const V1_ROUND_UPDATE_TRANSACTION: u8 = 5;
35// NOTE: 6 used to be reserved for serialized preview transactions,
36//       but they have never been serialized, so 6 is free for re-use
37
38// LEDGER TRANSACTION is not versioned, and can be extended with support
39// for new versions
40const LEDGER_TRANSACTION: u8 = 7;
41const V1_FLASH_TRANSACTION: u8 = 8;
42const V2_TRANSACTION_INTENT: u8 = 9;
43const V2_SIGNED_TRANSACTION_INTENT: u8 = 10;
44const V2_SUBINTENT: u8 = 11;
45const V2_NOTARIZED_TRANSACTION: u8 = 12;
46const V2_PARTIAL_TRANSACTION: u8 = 13;
47const V2_SIGNED_PARTIAL_TRANSACTION: u8 = 14;
48const V2_PREVIEW_TRANSACTION: u8 = 15;
49
50/// An enum of a variety of different transaction payload types.
51///
52/// Running `to_payload_bytes()` on each transaction type gives the same
53/// as Manifest SBOR encoding the variant of this enum.
54///
55/// For this reason, this type might see use in the Node's transaction
56/// parse API, and in other places where we want to decode or handle an
57/// arbitrary transaction payload.
58///
59/// All the transaction types also implement `ScryptoDescribe`, primarily
60/// so that they can derive `ScryptoSborAssertion` to ensure we don't change
61/// the types accidentally.
62#[derive(Clone, Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe, ScryptoSborAssertion)]
63#[sbor(impl_variant_traits)]
64#[sbor_assert(
65    // This sum type of all payload-convertible transactions is extensible, so
66    // we use `backwards_compatible` here. But most individual transaction models
67    // should themselves be `fixed`, e.g. NotarizedTransactionV1
68    backwards_compatible(
69        bottlenose = "FILE:any_transaction_payload_schema_bottlenose.txt",
70        cuttlefish = "FILE:any_transaction_payload_schema_cuttlefish.bin"
71    ),
72    settings(allow_name_changes)
73)]
74pub enum AnyTransaction {
75    #[sbor(discriminator(V1_INTENT))]
76    TransactionIntentV1(#[sbor(flatten)] IntentV1),
77    #[sbor(discriminator(V1_SIGNED_INTENT))]
78    SignedTransactionIntentV1(#[sbor(flatten)] SignedIntentV1),
79    #[sbor(discriminator(V1_NOTARIZED_TRANSACTION))]
80    NotarizedTransactionV1(#[sbor(flatten)] NotarizedTransactionV1),
81    #[sbor(discriminator(V1_SYSTEM_TRANSACTION))]
82    SystemTransactionV1(#[sbor(flatten)] SystemTransactionV1),
83    #[sbor(discriminator(V1_ROUND_UPDATE_TRANSACTION))]
84    RoundUpdateTransactionV1(#[sbor(flatten)] RoundUpdateTransactionV1),
85    #[sbor(discriminator(LEDGER_TRANSACTION))] // Not flattened because it's an enum
86    LedgerTransaction(LedgerTransaction),
87    #[sbor(discriminator(V1_FLASH_TRANSACTION))]
88    FlashTransactionV1(#[sbor(flatten)] FlashTransactionV1),
89    #[sbor(discriminator(V2_TRANSACTION_INTENT))]
90    TransactionIntentV2(#[sbor(flatten)] TransactionIntentV2),
91    #[sbor(discriminator(V2_SIGNED_TRANSACTION_INTENT))]
92    SignedTransactionIntentV2(#[sbor(flatten)] SignedTransactionIntentV2),
93    #[sbor(discriminator(V2_SUBINTENT))]
94    SubintentV2(#[sbor(flatten)] SubintentV2),
95    #[sbor(discriminator(V2_NOTARIZED_TRANSACTION))]
96    NotarizedTransactionV2(#[sbor(flatten)] NotarizedTransactionV2),
97    #[sbor(discriminator(V2_PARTIAL_TRANSACTION))]
98    PartialTransactionV2(#[sbor(flatten)] PartialTransactionV2),
99    #[sbor(discriminator(V2_SIGNED_PARTIAL_TRANSACTION))]
100    SignedPartialTransactionV2(#[sbor(flatten)] SignedPartialTransactionV2),
101    #[sbor(discriminator(V2_PREVIEW_TRANSACTION))]
102    PreviewTransactionV2(#[sbor(flatten)] PreviewTransactionV2),
103}
104
105#[cfg(test)]
106mod tests {
107    use radix_engine_interface::blueprints::resource::FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT;
108
109    use super::*;
110    use crate::manifest::e2e::tests::print_blob;
111    use crate::model::*;
112
113    fn hash_encoded_sbor_value<T: ManifestEncode>(value: T) -> Hash {
114        // Ignore the version byte
115        hash(&manifest_encode(&value).unwrap()[1..])
116    }
117
118    fn hash_encoded_sbor_value_body<T: ManifestEncode>(value: T) -> Hash {
119        // Ignore the version byte AND the value kind
120        hash(&manifest_encode(&value).unwrap()[2..])
121    }
122
123    /// This test demonstrates how the hashes and payloads are constructed in a valid user transaction.
124    /// It also provides an example payload which can be used in other implementations.
125    #[test]
126    pub fn v1_user_transaction_structure() {
127        let network = NetworkDefinition::simulator();
128        let preparation_settings = PreparationSettings::babylon();
129
130        // Create key pairs
131        let sig_1_private_key = Secp256k1PrivateKey::from_u64(1).unwrap();
132        let sig_2_private_key = Ed25519PrivateKey::from_u64(2).unwrap();
133        let notary_private_key = Ed25519PrivateKey::from_u64(3).unwrap();
134
135        //===================
136        // INTENT
137        //===================
138        let header_v1 = TransactionHeaderV1 {
139            network_id: network.id,
140            start_epoch_inclusive: Epoch::of(1),
141            end_epoch_exclusive: Epoch::of(5),
142            nonce: 0,
143            notary_public_key: notary_private_key.public_key().into(),
144            notary_is_signatory: false,
145            tip_percentage: 0,
146        };
147        let expected_header_hash = hash_encoded_sbor_value(&header_v1);
148
149        let instructions = vec![InstructionV1::DropAuthZoneProofs(DropAuthZoneProofs)];
150        let expected_instructions_hash = hash_encoded_sbor_value(&instructions);
151        let instructions_v1 = InstructionsV1(instructions);
152
153        let blob1: Vec<u8> = vec![0, 1, 2, 3];
154        let blob2: Vec<u8> = vec![5, 6];
155        let expected_blobs_hash =
156            hash([hash(&blob1).0.as_slice(), hash(&blob2).0.as_slice()].concat());
157
158        let blobs_v1 = BlobsV1 {
159            blobs: vec![BlobV1(blob1), BlobV1(blob2)],
160        };
161
162        let prepared_blobs_v1 = blobs_v1.prepare_partial(&preparation_settings).unwrap();
163        assert_eq!(prepared_blobs_v1.get_summary().hash, expected_blobs_hash);
164
165        let message_v1 = MessageV1::default();
166        let expected_attachments_hash = hash_encoded_sbor_value(&message_v1);
167
168        let intent_v1 = IntentV1 {
169            header: header_v1.clone(),
170            instructions: instructions_v1.clone(),
171            blobs: blobs_v1.clone(),
172            message: message_v1.clone(),
173        };
174        let expected_intent_hash = TransactionIntentHash::from_hash(hash(
175            [
176                [
177                    TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
178                    TransactionDiscriminator::V1Intent as u8,
179                ]
180                .as_slice(),
181                expected_header_hash.0.as_slice(),
182                expected_instructions_hash.0.as_slice(),
183                expected_blobs_hash.0.as_slice(),
184                expected_attachments_hash.0.as_slice(),
185            ]
186            .concat(),
187        ));
188
189        let raw_intent_payload = intent_v1.to_raw().unwrap();
190
191        println!();
192        print_blob("HC_INTENT", raw_intent_payload.as_slice());
193        print_blob("HC_INTENT_HASH", expected_intent_hash.0.as_slice());
194
195        IntentV1::from_raw(&raw_intent_payload).expect("Intent can be decoded");
196        let intent_as_versioned =
197            manifest_decode::<AnyTransaction>(raw_intent_payload.as_slice()).unwrap();
198        assert_eq!(
199            intent_as_versioned,
200            AnyTransaction::TransactionIntentV1(intent_v1.clone())
201        );
202
203        let prepared_intent =
204            PreparedIntentV1::prepare(&raw_intent_payload, &preparation_settings).unwrap();
205        assert_eq!(
206            expected_intent_hash,
207            prepared_intent.transaction_intent_hash()
208        );
209
210        let intent_hash = prepared_intent.transaction_intent_hash();
211
212        assert_eq!(
213            intent_hash.to_string(&TransactionHashBech32Encoder::for_simulator()),
214            "txid_sim16hm8cq74dyusrgy8xg6eg5ss0d3cte9hdj0dhudtzp6vvszh3vjq3amttp"
215        );
216        assert_eq!(
217            hex::encode(raw_intent_payload),
218            "4d220104210707f20a01000000000000000a05000000000000000900000000220101200720f381626e41e7027ea431bfe3009e94bdd25a746beec468948d6c3c7c5dc9a54b0100080000202201120020200207040001020307020506220000"
219        );
220
221        //===================
222        // SIGNED INTENT
223        //===================
224        let sig1 = sig_1_private_key.sign_with_public_key(&intent_hash);
225        let sig2 = sig_2_private_key.sign_with_public_key(&intent_hash);
226
227        let intent_signatures_v1 = IntentSignaturesV1 {
228            signatures: vec![IntentSignatureV1(sig1), IntentSignatureV1(sig2)],
229        };
230        let expected_intent_signatures_hash = hash_encoded_sbor_value(&intent_signatures_v1);
231
232        let signed_intent_v1 = SignedIntentV1 {
233            intent: intent_v1.clone(),
234            intent_signatures: intent_signatures_v1.clone(),
235        };
236        let expected_signed_intent_hash = SignedTransactionIntentHash::from_hash(hash(
237            [
238                [
239                    TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
240                    TransactionDiscriminator::V1SignedIntent as u8,
241                ]
242                .as_slice(),
243                intent_hash.0.as_slice(),
244                expected_intent_signatures_hash.0.as_slice(),
245            ]
246            .concat(),
247        ));
248
249        let raw_signed_intent = signed_intent_v1.to_raw().unwrap();
250
251        let signed_intent_as_versioned =
252            manifest_decode::<AnyTransaction>(raw_signed_intent.as_slice()).unwrap();
253        assert_eq!(
254            signed_intent_as_versioned,
255            AnyTransaction::SignedTransactionIntentV1(signed_intent_v1.clone())
256        );
257
258        let prepared_signed_intent =
259            PreparedSignedIntentV1::prepare(&raw_signed_intent, &preparation_settings).unwrap();
260        assert_eq!(
261            expected_signed_intent_hash,
262            prepared_signed_intent.signed_transaction_intent_hash()
263        );
264        assert_eq!(
265            intent_hash,
266            prepared_signed_intent.transaction_intent_hash()
267        );
268
269        let signed_intent_hash = expected_signed_intent_hash;
270
271        assert_eq!(
272            signed_intent_hash.to_string(&TransactionHashBech32Encoder::for_simulator()),
273            "signedintent_sim1dylyaqctdlpnr8768ve6gy6mhjryd5w46scepdx50nplyk64g28qcy3zxn"
274        );
275        assert_eq!(
276            hex::encode(raw_signed_intent),
277            "4d2202022104210707f20a01000000000000000a05000000000000000900000000220101200720f381626e41e7027ea431bfe3009e94bdd25a746beec468948d6c3c7c5dc9a54b01000800002022011200202002070400010203070205062200002022020001210120074100ffb4d3532977ad5f561d73ee8febbf4330812bb43063fd61a15e59ad233a13ea2f27b8eda06af0861b18108e4dae6301363b5b243ac1518f482e27f2f32f0bb701022007207422b9887598068e32c4448a949adb290d0f4e35b9e01b0ee5f1a1e600fe26742101200740f0587aa712a637c84b0b2bc929c14cb2ccb3846c330434459205a11be5ff610cadfdbf33fa12b98d8e947f33a350a84068e710672753cdc33315c400db9c4e0f"
278        );
279
280        //======================
281        // NOTARIZED TRANSACTION
282        //======================
283        let notary_signature = notary_private_key.sign(&signed_intent_hash);
284
285        let notary_signature_v1 = NotarySignatureV1(notary_signature.into());
286        let expected_notary_signature_v1_hash = hash_encoded_sbor_value(&notary_signature_v1);
287
288        let notarized_transaction_v1 = NotarizedTransactionV1 {
289            signed_intent: signed_intent_v1.clone(),
290            notary_signature: notary_signature_v1.clone(),
291        };
292        let expected_notarized_transaction_hash = NotarizedTransactionHash::from_hash(hash(
293            [
294                [
295                    TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
296                    TransactionDiscriminator::V1Notarized as u8,
297                ]
298                .as_slice(),
299                signed_intent_hash.0.as_slice(),
300                expected_notary_signature_v1_hash.0.as_slice(),
301            ]
302            .concat(),
303        ));
304
305        let raw_notarized_transaction = notarized_transaction_v1.to_raw().unwrap();
306        NotarizedTransactionV1::from_raw(&raw_notarized_transaction)
307            .expect("NotarizedTransaction can be decoded");
308        let notarized_transaction_as_versioned =
309            manifest_decode::<AnyTransaction>(raw_notarized_transaction.as_slice()).unwrap();
310        assert_eq!(
311            notarized_transaction_as_versioned,
312            AnyTransaction::NotarizedTransactionV1(notarized_transaction_v1)
313        );
314
315        let prepared_notarized_transaction = PreparedNotarizedTransactionV1::prepare(
316            &raw_notarized_transaction,
317            &preparation_settings,
318        )
319        .unwrap();
320        assert_eq!(
321            expected_notarized_transaction_hash,
322            prepared_notarized_transaction.notarized_transaction_hash()
323        );
324        let notarized_transaction_hash = expected_notarized_transaction_hash;
325        assert_eq!(
326            signed_intent_hash,
327            prepared_notarized_transaction.signed_transaction_intent_hash()
328        );
329        assert_eq!(
330            intent_hash,
331            prepared_notarized_transaction.transaction_intent_hash()
332        );
333
334        assert_eq!(
335            notarized_transaction_hash.to_string(&TransactionHashBech32Encoder::for_simulator()),
336            "notarizedtransaction_sim1lhfnzp027gt7ducszxmkl02qpp5lpx25npqwxkrk2qqyhs08raksacmd94"
337        );
338        assert_eq!(
339            hex::encode(raw_notarized_transaction),
340            "4d22030221022104210707f20a01000000000000000a05000000000000000900000000220101200720f381626e41e7027ea431bfe3009e94bdd25a746beec468948d6c3c7c5dc9a54b01000800002022011200202002070400010203070205062200002022020001210120074100ffb4d3532977ad5f561d73ee8febbf4330812bb43063fd61a15e59ad233a13ea2f27b8eda06af0861b18108e4dae6301363b5b243ac1518f482e27f2f32f0bb701022007207422b9887598068e32c4448a949adb290d0f4e35b9e01b0ee5f1a1e600fe26742101200740f0587aa712a637c84b0b2bc929c14cb2ccb3846c330434459205a11be5ff610cadfdbf33fa12b98d8e947f33a350a84068e710672753cdc33315c400db9c4e0f2201012101200740321bfd17cac75d0b16fe6fd5aa9bb3e2beaf6521af4607f28815c8bd08718de8078a3fd75750354c400e1ea33cc8986853af6115bc43530cc0550ec9b2696a06"
341        );
342    }
343
344    /// This test demonstrates how the hashes and payloads are constructed in a valid user transaction.
345    /// It also provides an example payload which can be used in other implementations.
346    #[test]
347    pub fn v2_notarized_transaction_structure() {
348        let network = NetworkDefinition::simulator();
349
350        // TODO - add more of the structure
351        create_checked_childless_subintent_v2(&network);
352    }
353
354    fn create_checked_childless_subintent_v2(
355        network: &NetworkDefinition,
356    ) -> (SubintentV2, SubintentHash) {
357        let (header, expected_header_hash) = create_intent_header_v2(network);
358        let (blobs, expected_blobs_hash) = create_blobs_v1();
359        let (instructions, expected_instructions_hash) =
360            create_childless_subintent_instructions_v2();
361        let (message, expected_message_hash) = create_message_v2();
362        let (child_intent_constraints, expected_constraints_hash) =
363            create_childless_child_intents_v2();
364
365        let subintent = SubintentV2 {
366            intent_core: IntentCoreV2 {
367                header,
368                instructions,
369                blobs,
370                message,
371                children: child_intent_constraints,
372            },
373        };
374        let expected_intent_core_hash = hash(
375            [
376                expected_header_hash.as_slice(),
377                expected_blobs_hash.as_slice(),
378                expected_message_hash.as_slice(),
379                expected_constraints_hash.as_slice(),
380                expected_instructions_hash.as_slice(),
381            ]
382            .concat(),
383        );
384        let expected_subintent_hash = SubintentHash(hash(
385            [
386                [
387                    TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
388                    TransactionDiscriminator::V2Subintent as u8,
389                ]
390                .as_slice(),
391                expected_intent_core_hash.as_slice(),
392            ]
393            .concat(),
394        ));
395
396        let prepared = subintent
397            .prepare(PreparationSettings::latest_ref())
398            .unwrap();
399        let actual_subintent_hash = prepared.subintent_hash();
400        assert_eq!(expected_subintent_hash, actual_subintent_hash);
401        assert_eq!(
402            expected_subintent_hash.to_string(&TransactionHashBech32Encoder::for_simulator()),
403            "subtxid_sim1ree59h2u2sguzl6g72pn7q9hpe3r28l95c05f2rfe7cgfp4sgmwqx5l3mu",
404        );
405
406        (subintent, actual_subintent_hash)
407    }
408
409    fn create_intent_header_v2(network: &NetworkDefinition) -> (IntentHeaderV2, Hash) {
410        let intent_header = IntentHeaderV2 {
411            network_id: network.id,
412            start_epoch_inclusive: Epoch::of(1),
413            end_epoch_exclusive: Epoch::of(10),
414            min_proposer_timestamp_inclusive: None,
415            max_proposer_timestamp_exclusive: Some(Instant::new(0)),
416            intent_discriminator: 0,
417        };
418        let expected_hash = hash_encoded_sbor_value_body(&intent_header);
419        let actual_hash = intent_header
420            .prepare_partial(PreparationSettings::latest_ref())
421            .unwrap()
422            .get_summary()
423            .hash;
424        assert_eq!(expected_hash, actual_hash);
425        (intent_header, expected_hash)
426    }
427
428    fn create_blobs_v1() -> (BlobsV1, Hash) {
429        let blob1: Vec<u8> = vec![0, 1, 2, 3];
430        let blob2: Vec<u8> = vec![5, 6];
431        let expected_hash = hash([hash(&blob1).0.as_slice(), hash(&blob2).0.as_slice()].concat());
432
433        let blobs_v1 = BlobsV1 {
434            blobs: vec![BlobV1(blob1), BlobV1(blob2)],
435        };
436
437        let actual_hash = blobs_v1
438            .prepare_partial(PreparationSettings::latest_ref())
439            .unwrap()
440            .get_summary()
441            .hash;
442        assert_eq!(expected_hash, actual_hash);
443
444        (blobs_v1, expected_hash)
445    }
446
447    fn create_childless_subintent_instructions_v2() -> (InstructionsV2, Hash) {
448        let instructions = InstructionsV2::from(vec![]);
449        let expected_hash = hash_encoded_sbor_value_body(&instructions);
450
451        let actual_hash = instructions
452            .prepare_partial(PreparationSettings::latest_ref())
453            .unwrap()
454            .get_summary()
455            .hash;
456        assert_eq!(expected_hash, actual_hash);
457
458        (instructions, expected_hash)
459    }
460
461    fn create_message_v2() -> (MessageV2, Hash) {
462        let message = MessageV2::Plaintext(PlaintextMessageV1::text("Hello world!"));
463        let expected_hash = hash_encoded_sbor_value_body(&message);
464
465        let actual_hash = message
466            .prepare_partial(PreparationSettings::latest_ref())
467            .unwrap()
468            .get_summary()
469            .hash;
470        assert_eq!(expected_hash, actual_hash);
471
472        (message, expected_hash)
473    }
474
475    fn create_childless_child_intents_v2() -> (ChildSubintentSpecifiersV2, Hash) {
476        let children: ChildSubintentSpecifiersV2 = ChildSubintentSpecifiersV2 {
477            children: Default::default(),
478        };
479        // Concatenation of all hashes
480        let empty: [u8; 0] = [];
481        let expected_hash = hash(&empty);
482
483        let actual_hash = children
484            .prepare_partial(PreparationSettings::latest_ref())
485            .unwrap()
486            .get_summary()
487            .hash;
488        assert_eq!(expected_hash, actual_hash);
489
490        (children, expected_hash)
491    }
492
493    /// This test demonstrates how the hashes and payloads are constructed in a valid system transaction.
494    /// A system transaction can be embedded into the node's LedgerTransaction structure, eg as part of Genesis
495    #[test]
496    pub fn v1_system_transaction_structure() {
497        let instructions = vec![InstructionV1::DropAuthZoneProofs(DropAuthZoneProofs)];
498        let expected_instructions_hash = hash_encoded_sbor_value(&instructions);
499        let instructions_v1 = InstructionsV1(instructions);
500
501        let blob1: Vec<u8> = vec![0, 1, 2, 3];
502        let blob2: Vec<u8> = vec![5, 6];
503        let expected_blobs_hash =
504            hash([hash(&blob1).0.as_slice(), hash(&blob2).0.as_slice()].concat());
505
506        let blobs_v1 = BlobsV1 {
507            blobs: vec![BlobV1(blob1), BlobV1(blob2)],
508        };
509
510        let prepared_blobs_v1 = blobs_v1
511            .prepare_partial(PreparationSettings::latest_ref())
512            .unwrap();
513        assert_eq!(prepared_blobs_v1.get_summary().hash, expected_blobs_hash);
514
515        let pre_allocated_addresses_v1 = vec![PreAllocatedAddress {
516            blueprint_id: BlueprintId::new(&RESOURCE_PACKAGE, FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT),
517            address: XRD.into(),
518        }];
519        let expected_preallocated_addresses_hash =
520            hash_encoded_sbor_value(&pre_allocated_addresses_v1);
521
522        let hash_for_execution = hash(format!("Pretend genesis transaction"));
523
524        let system_transaction_v1 = SystemTransactionV1 {
525            instructions: instructions_v1.clone(),
526            blobs: blobs_v1.clone(),
527            pre_allocated_addresses: pre_allocated_addresses_v1.clone(),
528            hash_for_execution: hash_for_execution.clone(),
529        };
530        let expected_system_transaction_hash = SystemTransactionHash::from_hash(hash(
531            [
532                [
533                    TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
534                    TransactionDiscriminator::V1System as u8,
535                ]
536                .as_slice(),
537                expected_instructions_hash.0.as_slice(),
538                expected_blobs_hash.0.as_slice(),
539                expected_preallocated_addresses_hash.0.as_slice(),
540                hash_for_execution.0.as_slice(),
541            ]
542            .concat(),
543        ));
544
545        let raw_system_transaction = system_transaction_v1.to_raw().unwrap();
546        SystemTransactionV1::from_raw(&raw_system_transaction)
547            .expect("SystemTransaction can be decoded");
548        let system_transaction_as_versioned =
549            manifest_decode::<AnyTransaction>(&raw_system_transaction.as_slice()).unwrap();
550        assert_eq!(
551            system_transaction_as_versioned,
552            AnyTransaction::SystemTransactionV1(system_transaction_v1)
553        );
554
555        let prepared_system_transaction = PreparedSystemTransactionV1::prepare(
556            &raw_system_transaction,
557            PreparationSettings::latest_ref(),
558        )
559        .unwrap();
560
561        assert_eq!(
562            expected_system_transaction_hash,
563            prepared_system_transaction.system_transaction_hash()
564        );
565        assert_eq!(
566            expected_system_transaction_hash
567                .to_string(&TransactionHashBech32Encoder::for_simulator()),
568            "systemtransaction_sim14yf4hrcuqw9y8xrje8jr7h8y3fwnsg9y6nts2f5ru6r8s3yvgguq2da744"
569        );
570        assert_eq!(
571            hex::encode(raw_system_transaction),
572            "4d22040420220112002020020704000102030702050620210102210280000d906318c6318c61e603c64c6318c6318cf7be913d63aafbc6318c6318c60c1746756e6769626c655265736f757263654d616e6167657280005da66318c6318c61f5a61b4c6318c6318cf794aa8d295f14e6318c6318c62007207646fcb3e6a2dbf0fd4830933c54928d3e8dafaf9f704afdae56336fc67aae0d"
573        );
574    }
575}