1use crate::internal_prelude::*;
2
3#[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;
35const 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#[derive(Clone, Debug, Eq, PartialEq, ManifestSbor, ScryptoDescribe, ScryptoSborAssertion)]
63#[sbor(impl_variant_traits)]
64#[sbor_assert(
65 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))] 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 #[deprecated = "Should only be used by transaction v1, because it's less flexible than hash_encoded_sbor_value_body"]
114 fn hash_encoded_sbor_value<T: ManifestEncode>(value: T) -> Hash {
115 hash(&manifest_encode(&value).unwrap()[1..])
117 }
118
119 fn hash_encoded_sbor_value_body<T: ManifestEncode>(value: T) -> Hash {
120 hash(&manifest_encode(&value).unwrap()[2..])
122 }
123
124 fn hash_contatenated_hashes<H: Into<Hash>>(hashes: impl IntoIterator<Item = H>) -> Hash {
125 let concatenated_hashes: Vec<u8> = hashes
126 .into_iter()
127 .flat_map(|h| Into::<Hash>::into(h).0)
128 .collect();
129 hash(concatenated_hashes)
130 }
131
132 fn hash_from_partial_prepare(value: &impl TransactionPartialPrepare) -> Hash {
133 value
134 .prepare_partial(PreparationSettings::latest_ref())
135 .unwrap()
136 .get_summary()
137 .hash
138 }
139
140 #[test]
143 #[allow(deprecated)] pub fn v1_user_transaction_structure() {
145 let network = NetworkDefinition::simulator();
146 let preparation_settings = PreparationSettings::babylon();
147
148 let sig_1_private_key = Secp256k1PrivateKey::from_u64(1).unwrap();
150 let sig_2_private_key = Ed25519PrivateKey::from_u64(2).unwrap();
151 let notary_private_key = Ed25519PrivateKey::from_u64(3).unwrap();
152
153 let header_v1 = TransactionHeaderV1 {
157 network_id: network.id,
158 start_epoch_inclusive: Epoch::of(1),
159 end_epoch_exclusive: Epoch::of(5),
160 nonce: 0,
161 notary_public_key: notary_private_key.public_key().into(),
162 notary_is_signatory: false,
163 tip_percentage: 0,
164 };
165 let expected_header_hash = hash_encoded_sbor_value(&header_v1);
166
167 let instructions = vec![InstructionV1::DropAuthZoneProofs(DropAuthZoneProofs)];
168 let expected_instructions_hash = hash_encoded_sbor_value(&instructions);
169 let instructions_v1 = InstructionsV1(instructions);
170
171 let blob1: Vec<u8> = vec![0, 1, 2, 3];
172 let blob2: Vec<u8> = vec![5, 6];
173 let expected_blobs_hash =
174 hash([hash(&blob1).0.as_slice(), hash(&blob2).0.as_slice()].concat());
175
176 let blobs_v1 = BlobsV1 {
177 blobs: vec![BlobV1(blob1), BlobV1(blob2)],
178 };
179
180 let prepared_blobs_v1 = blobs_v1.prepare_partial(&preparation_settings).unwrap();
181 assert_eq!(prepared_blobs_v1.get_summary().hash, expected_blobs_hash);
182
183 let message_v1 = MessageV1::default();
184 let expected_attachments_hash = hash_encoded_sbor_value(&message_v1);
185
186 let intent_v1 = IntentV1 {
187 header: header_v1.clone(),
188 instructions: instructions_v1.clone(),
189 blobs: blobs_v1.clone(),
190 message: message_v1.clone(),
191 };
192 let expected_intent_hash = TransactionIntentHash::from_hash(hash(
193 [
194 [
195 TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
196 TransactionDiscriminator::V1Intent as u8,
197 ]
198 .as_slice(),
199 expected_header_hash.0.as_slice(),
200 expected_instructions_hash.0.as_slice(),
201 expected_blobs_hash.0.as_slice(),
202 expected_attachments_hash.0.as_slice(),
203 ]
204 .concat(),
205 ));
206
207 let raw_intent_payload = intent_v1.to_raw().unwrap();
208
209 println!();
210 print_blob("HC_INTENT", raw_intent_payload.as_slice());
211 print_blob("HC_INTENT_HASH", expected_intent_hash.0.as_slice());
212
213 IntentV1::from_raw(&raw_intent_payload).expect("Intent can be decoded");
214 let intent_as_versioned =
215 manifest_decode::<AnyTransaction>(raw_intent_payload.as_slice()).unwrap();
216 assert_eq!(
217 intent_as_versioned,
218 AnyTransaction::TransactionIntentV1(intent_v1.clone())
219 );
220
221 let prepared_intent =
222 PreparedIntentV1::prepare(&raw_intent_payload, &preparation_settings).unwrap();
223 assert_eq!(
224 expected_intent_hash,
225 prepared_intent.transaction_intent_hash()
226 );
227
228 let intent_hash = prepared_intent.transaction_intent_hash();
229
230 assert_eq!(
231 intent_hash.to_string(&TransactionHashBech32Encoder::for_simulator()),
232 "txid_sim16hm8cq74dyusrgy8xg6eg5ss0d3cte9hdj0dhudtzp6vvszh3vjq3amttp"
233 );
234 assert_eq!(
235 hex::encode(raw_intent_payload),
236 "4d220104210707f20a01000000000000000a05000000000000000900000000220101200720f381626e41e7027ea431bfe3009e94bdd25a746beec468948d6c3c7c5dc9a54b0100080000202201120020200207040001020307020506220000"
237 );
238
239 let sig1 = sig_1_private_key.sign_with_public_key(&intent_hash);
243 let sig2 = sig_2_private_key.sign_with_public_key(&intent_hash);
244
245 let intent_signatures_v1 = IntentSignaturesV1 {
246 signatures: vec![IntentSignatureV1(sig1), IntentSignatureV1(sig2)],
247 };
248 let expected_intent_signatures_hash = hash_encoded_sbor_value(&intent_signatures_v1);
249
250 let signed_intent_v1 = SignedIntentV1 {
251 intent: intent_v1.clone(),
252 intent_signatures: intent_signatures_v1.clone(),
253 };
254 let expected_signed_intent_hash = SignedTransactionIntentHash::from_hash(hash(
255 [
256 [
257 TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
258 TransactionDiscriminator::V1SignedIntent as u8,
259 ]
260 .as_slice(),
261 intent_hash.0.as_slice(),
262 expected_intent_signatures_hash.0.as_slice(),
263 ]
264 .concat(),
265 ));
266
267 let raw_signed_intent = signed_intent_v1.to_raw().unwrap();
268
269 let signed_intent_as_versioned =
270 manifest_decode::<AnyTransaction>(raw_signed_intent.as_slice()).unwrap();
271 assert_eq!(
272 signed_intent_as_versioned,
273 AnyTransaction::SignedTransactionIntentV1(signed_intent_v1.clone())
274 );
275
276 let prepared_signed_intent =
277 PreparedSignedIntentV1::prepare(&raw_signed_intent, &preparation_settings).unwrap();
278 assert_eq!(
279 expected_signed_intent_hash,
280 prepared_signed_intent.signed_transaction_intent_hash()
281 );
282 assert_eq!(
283 intent_hash,
284 prepared_signed_intent.transaction_intent_hash()
285 );
286
287 let signed_intent_hash = expected_signed_intent_hash;
288
289 assert_eq!(
290 signed_intent_hash.to_string(&TransactionHashBech32Encoder::for_simulator()),
291 "signedintent_sim1dylyaqctdlpnr8768ve6gy6mhjryd5w46scepdx50nplyk64g28qcy3zxn"
292 );
293 assert_eq!(
294 hex::encode(raw_signed_intent),
295 "4d2202022104210707f20a01000000000000000a05000000000000000900000000220101200720f381626e41e7027ea431bfe3009e94bdd25a746beec468948d6c3c7c5dc9a54b01000800002022011200202002070400010203070205062200002022020001210120074100ffb4d3532977ad5f561d73ee8febbf4330812bb43063fd61a15e59ad233a13ea2f27b8eda06af0861b18108e4dae6301363b5b243ac1518f482e27f2f32f0bb701022007207422b9887598068e32c4448a949adb290d0f4e35b9e01b0ee5f1a1e600fe26742101200740f0587aa712a637c84b0b2bc929c14cb2ccb3846c330434459205a11be5ff610cadfdbf33fa12b98d8e947f33a350a84068e710672753cdc33315c400db9c4e0f"
296 );
297
298 let notary_signature = notary_private_key.sign(signed_intent_hash);
302
303 let notary_signature_v1 = NotarySignatureV1(notary_signature.into());
304 let expected_notary_signature_v1_hash = hash_encoded_sbor_value(¬ary_signature_v1);
305
306 let notarized_transaction_v1 = NotarizedTransactionV1 {
307 signed_intent: signed_intent_v1.clone(),
308 notary_signature: notary_signature_v1.clone(),
309 };
310 let expected_notarized_transaction_hash = NotarizedTransactionHash::from_hash(hash(
311 [
312 [
313 TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
314 TransactionDiscriminator::V1Notarized as u8,
315 ]
316 .as_slice(),
317 signed_intent_hash.0.as_slice(),
318 expected_notary_signature_v1_hash.0.as_slice(),
319 ]
320 .concat(),
321 ));
322
323 let raw_notarized_transaction = notarized_transaction_v1.to_raw().unwrap();
324 NotarizedTransactionV1::from_raw(&raw_notarized_transaction)
325 .expect("NotarizedTransaction can be decoded");
326 let notarized_transaction_as_versioned =
327 manifest_decode::<AnyTransaction>(raw_notarized_transaction.as_slice()).unwrap();
328 assert_eq!(
329 notarized_transaction_as_versioned,
330 AnyTransaction::NotarizedTransactionV1(notarized_transaction_v1)
331 );
332
333 let prepared_notarized_transaction = PreparedNotarizedTransactionV1::prepare(
334 &raw_notarized_transaction,
335 &preparation_settings,
336 )
337 .unwrap();
338 assert_eq!(
339 expected_notarized_transaction_hash,
340 prepared_notarized_transaction.notarized_transaction_hash()
341 );
342 let notarized_transaction_hash = expected_notarized_transaction_hash;
343 assert_eq!(
344 signed_intent_hash,
345 prepared_notarized_transaction.signed_transaction_intent_hash()
346 );
347 assert_eq!(
348 intent_hash,
349 prepared_notarized_transaction.transaction_intent_hash()
350 );
351
352 assert_eq!(
353 notarized_transaction_hash.to_string(&TransactionHashBech32Encoder::for_simulator()),
354 "notarizedtransaction_sim1lhfnzp027gt7ducszxmkl02qpp5lpx25npqwxkrk2qqyhs08raksacmd94"
355 );
356 assert_eq!(
357 hex::encode(raw_notarized_transaction),
358 "4d22030221022104210707f20a01000000000000000a05000000000000000900000000220101200720f381626e41e7027ea431bfe3009e94bdd25a746beec468948d6c3c7c5dc9a54b01000800002022011200202002070400010203070205062200002022020001210120074100ffb4d3532977ad5f561d73ee8febbf4330812bb43063fd61a15e59ad233a13ea2f27b8eda06af0861b18108e4dae6301363b5b243ac1518f482e27f2f32f0bb701022007207422b9887598068e32c4448a949adb290d0f4e35b9e01b0ee5f1a1e600fe26742101200740f0587aa712a637c84b0b2bc929c14cb2ccb3846c330434459205a11be5ff610cadfdbf33fa12b98d8e947f33a350a84068e710672753cdc33315c400db9c4e0f2201012101200740321bfd17cac75d0b16fe6fd5aa9bb3e2beaf6521af4607f28815c8bd08718de8078a3fd75750354c400e1ea33cc8986853af6115bc43530cc0550ec9b2696a06"
359 );
360 }
361
362 #[test]
365 pub fn v2_notarized_transaction_structure() {
366 let network = NetworkDefinition::simulator();
367
368 let (signed_transaction_intent, signed_transaction_intent_hash) =
369 create_signed_transaction_intent_v2(&network);
370 let (notary_signature, notary_signature_hash) =
371 create_notary_signature_v2(signed_transaction_intent_hash);
372
373 let notarized_transaction = NotarizedTransactionV2 {
374 signed_transaction_intent,
375 notary_signature,
376 };
377 let expected_hash = NotarizedTransactionHash(hash(
378 [
379 [
380 TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
381 TransactionDiscriminator::V2Notarized as u8,
382 ]
383 .as_slice(),
384 signed_transaction_intent_hash.0.as_slice(),
385 notary_signature_hash.0.as_slice(),
386 ]
387 .concat(),
388 ));
389 let raw = notarized_transaction.to_raw().unwrap();
390
391 let prepared_transaction = notarized_transaction
392 .prepare(&PreparationSettings::latest())
393 .unwrap();
394 let actual_transaction_intent_hash = prepared_transaction.transaction_intent_hash();
395 let actual_signed_transaction_intent_hash =
396 prepared_transaction.signed_transaction_intent_hash();
397 let notarized_transaction_hash = prepared_transaction.notarized_transaction_hash();
398
399 assert_eq!(expected_hash, notarized_transaction_hash);
400 assert_eq!(
401 notarized_transaction_hash.to_string(&TransactionHashBech32Encoder::for_simulator()),
402 "notarizedtransaction_sim1qh37lkr547jgv5zfvlkq4njdhn62m2sg09k6njmkuma7u2hd4zasrmhyew",
403 );
404 assert_eq!(
405 actual_signed_transaction_intent_hash
406 .to_string(&TransactionHashBech32Encoder::for_simulator()),
407 "signedintent_sim1z2at9wmfh7pcx7ad0c4npyv3xn3mecf2gyehwd6g6w99v56ntfsq4k92yx",
408 );
409 assert_eq!(
410 actual_transaction_intent_hash
411 .to_string(&TransactionHashBech32Encoder::for_simulator()),
412 "txid_sim1v7xlgxkrk59qekpj53x8jul0lml0r4nzn3yfmv4jd5ysjewmkaust5l3t2",
413 );
414 assert_eq!(
415 hex::encode(raw.as_slice()),
416 "4d220c0221032103210322010120072009b3f25a3a1839f46ddb09b068271811f6f00a79246fb24e7a808a9e46d6075d010009000000002105210607f20a01000000000000000a0a000000000000002200002201010500000000000000000a00000000000000002020020704000102030702050622010121020c0a746578742f706c61696e2200010c0c48656c6c6f20776f726c64212020010720b37d9be9fe7362e9f01a828af77a3298758ac7d43be750575befdbd395c28918202201610209000000002100202101012105210607f20a01000000000000000a0a000000000000002200002201010500000000000000000a00000000000000002020020704000102030702050622010121020c0a746578742f706c61696e2200010c0c48656c6c6f20776f726c64212020002022016001210020220101022007204d956b5eb1147b3a80c40170e340e2918d2a9f33bdb529c54401e3ed80a4e70a2101200740e04f0e563d71ca150d900d75538d2253dff0f77d86c8ecfa4dcd25ac94de5a4ed27d76ac95c3ee8ebdcc1da52df6d1ca5f265bc1f973f631bc753e4146b3aa0c20200122010102200720c561fa9f643fe5c60113cce9db282fde2b9e5ca5fc6b6fc0d1679bb339c9f72f2101200740860417490e96c91addd5a390f5f1bcd2697535f23a947d2337b291a7b86611f56cc3aa0606ac8b8cba98381c35ef9a1f655362b18764eb90b1d8b814ec17f40e2201012101200740975a47326156a7818b4776e3e455a67c906c34eda7a9c9bb9688c77664ed9679c78aa9e33740aa1d3631b89119071a3feaf02b650799da64da7f659d107db905"
417 );
418
419 prepared_transaction
421 .validate(&TransactionValidator::new_for_latest_simulator())
422 .unwrap();
423 }
424
425 fn create_notary_signature_v2(
426 hash_to_sign: SignedTransactionIntentHash,
427 ) -> (NotarySignatureV2, Hash) {
428 let notary_signature = NotarySignatureV2(
429 TransactionV2Builder::testing_default_notary()
430 .sign_without_public_key(hash_to_sign.as_hash()),
431 );
432 let expected_hash = hash_encoded_sbor_value_body(¬ary_signature);
433 let actual_hash = hash_from_partial_prepare(¬ary_signature);
434 assert_eq!(expected_hash, actual_hash);
435 (notary_signature, actual_hash)
436 }
437
438 fn create_signed_transaction_intent_v2(
439 network: &NetworkDefinition,
440 ) -> (SignedTransactionIntentV2, SignedTransactionIntentHash) {
441 let (transaction_intent, transaction_intent_hash, subintent_hash) =
442 create_transaction_intent_v2(network);
443 let (transaction_intent_signatures, transaction_intent_signatures_hash) =
444 create_intent_signatures_v2(vec![2313], transaction_intent_hash);
445 let (non_root_subintent_signatures, non_root_subintent_signatures_hash) =
446 create_non_root_subintent_signatures(vec![subintent_hash]);
447
448 let signed = SignedTransactionIntentV2 {
449 transaction_intent,
450 transaction_intent_signatures,
451 non_root_subintent_signatures,
452 };
453
454 let expected_hash = SignedTransactionIntentHash(hash(
455 [
456 [
457 TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
458 TransactionDiscriminator::V2SignedTransactionIntent as u8,
459 ]
460 .as_slice(),
461 transaction_intent_hash.0.as_slice(),
462 transaction_intent_signatures_hash.0.as_slice(),
463 non_root_subintent_signatures_hash.0.as_slice(),
464 ]
465 .concat(),
466 ));
467
468 let prepared = signed.prepare(&PreparationSettings::latest()).unwrap();
469 let actual_hash = prepared.signed_transaction_intent_hash();
470 assert_eq!(
471 actual_hash.to_string(&TransactionHashBech32Encoder::for_simulator()),
472 "signedintent_sim1z2at9wmfh7pcx7ad0c4npyv3xn3mecf2gyehwd6g6w99v56ntfsq4k92yx",
473 );
474 assert_eq!(expected_hash, actual_hash);
475
476 (signed, actual_hash)
477 }
478
479 fn create_non_root_subintent_signatures(
480 subintent_hashes: Vec<SubintentHash>,
481 ) -> (NonRootSubintentSignaturesV2, Hash) {
482 let (batches, batch_hashes): (Vec<_>, Vec<_>) = subintent_hashes
483 .into_iter()
484 .enumerate()
485 .map(|(i, subintent_hash)| {
486 create_intent_signatures_v2(vec![(i * 100 + 42) as u64], subintent_hash)
487 })
488 .unzip();
489 let signature_batches = NonRootSubintentSignaturesV2 {
490 by_subintent: batches,
491 };
492 let expected_hash = hash_contatenated_hashes(batch_hashes);
493 let actual_hash = hash_from_partial_prepare(&signature_batches);
494 assert_eq!(expected_hash, actual_hash);
495 (signature_batches, expected_hash)
496 }
497
498 fn create_intent_signatures_v2(
499 key_sources: Vec<u64>,
500 intent_hash: impl Into<IntentHash>,
501 ) -> (IntentSignaturesV2, Hash) {
502 let hash_to_sign = intent_hash.into().into_hash();
503 let signatures = IntentSignaturesV2 {
504 signatures: key_sources
505 .into_iter()
506 .map(|key_source| {
507 create_intent_signature_v1(
508 Ed25519PrivateKey::from_u64(key_source).unwrap(),
509 &hash_to_sign,
510 )
511 })
512 .collect(),
513 };
514 let expected_hash = hash_encoded_sbor_value_body(&signatures);
515 let actual_hash = hash_from_partial_prepare(&signatures);
516 assert_eq!(expected_hash, actual_hash);
517 (signatures, actual_hash)
518 }
519
520 fn create_intent_signature_v1(signer: impl Signer, hash_to_sign: &Hash) -> IntentSignatureV1 {
521 let signature = signer.sign_with_public_key(hash_to_sign);
522 IntentSignatureV1(signature)
523 }
524
525 fn create_transaction_intent_v2(
526 network: &NetworkDefinition,
527 ) -> (TransactionIntentV2, TransactionIntentHash, SubintentHash) {
528 let (subintent_1, subintent_1_hash) = create_checked_childless_subintent_v2(network);
529 let (non_root_subintents, non_root_subintents_hash) =
530 create_non_root_subintents_v2(vec![subintent_1], vec![subintent_1_hash]);
531
532 let (transaction_header, transaction_header_hash) = create_transaction_header_v2();
533 let (root_intent_core, root_intent_core_hash) = create_intent_core_v2(
534 &NetworkDefinition::simulator(),
535 vec![InstructionV2::YieldToChild(YieldToChild::empty(0))],
536 vec![subintent_1_hash],
537 );
538
539 let expected_transaction_intent_hash = TransactionIntentHash(hash(
540 [
541 [
542 TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
543 TransactionDiscriminator::V2TransactionIntent as u8,
544 ]
545 .as_slice(),
546 transaction_header_hash.as_slice(),
547 root_intent_core_hash.as_slice(),
548 non_root_subintents_hash.as_slice(),
549 ]
550 .concat(),
551 ));
552
553 let transaction_intent = TransactionIntentV2 {
554 transaction_header,
555 root_intent_core,
556 non_root_subintents,
557 };
558
559 let actual_hash = transaction_intent
560 .prepare(PreparationSettings::latest_ref())
561 .unwrap()
562 .transaction_intent_hash();
563
564 assert_eq!(expected_transaction_intent_hash, actual_hash);
565 assert_eq!(
566 expected_transaction_intent_hash
567 .to_string(&TransactionHashBech32Encoder::for_simulator()),
568 "txid_sim1v7xlgxkrk59qekpj53x8jul0lml0r4nzn3yfmv4jd5ysjewmkaust5l3t2",
569 );
570
571 (transaction_intent, actual_hash, subintent_1_hash)
572 }
573
574 fn create_transaction_header_v2() -> (TransactionHeaderV2, Hash) {
575 let transaction_header = TransactionHeaderV2 {
576 notary_public_key: TransactionV2Builder::testing_default_notary()
577 .public_key()
578 .into(),
579 notary_is_signatory: false,
580 tip_basis_points: 0,
581 };
582 let expected_hash = hash_encoded_sbor_value_body(&transaction_header);
583 let actual_hash = hash_from_partial_prepare(&transaction_header);
584 assert_eq!(expected_hash, actual_hash);
585 (transaction_header, expected_hash)
586 }
587
588 fn create_non_root_subintents_v2(
589 subintents: Vec<SubintentV2>,
590 hashes: Vec<SubintentHash>,
591 ) -> (NonRootSubintentsV2, Hash) {
592 let non_root_subintents = NonRootSubintentsV2(subintents);
593
594 let expected_hash = hash_contatenated_hashes(hashes);
595 let actual_hash = hash_from_partial_prepare(&non_root_subintents);
596 assert_eq!(expected_hash, actual_hash);
597
598 (non_root_subintents, expected_hash)
599 }
600
601 fn create_checked_childless_subintent_v2(
602 network: &NetworkDefinition,
603 ) -> (SubintentV2, SubintentHash) {
604 let (intent_core, intent_core_hash) = create_intent_core_v2(
605 network,
606 vec![InstructionV2::YieldToParent(YieldToParent::empty())],
607 vec![],
608 );
609
610 let subintent = SubintentV2 { intent_core };
611
612 let expected_subintent_hash = SubintentHash(hash(
613 [
614 [
615 TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
616 TransactionDiscriminator::V2Subintent as u8,
617 ]
618 .as_slice(),
619 intent_core_hash.as_slice(),
620 ]
621 .concat(),
622 ));
623
624 let prepared = subintent
625 .prepare(PreparationSettings::latest_ref())
626 .unwrap();
627 let actual_subintent_hash = prepared.subintent_hash();
628 assert_eq!(expected_subintent_hash, actual_subintent_hash);
629 assert_eq!(
630 expected_subintent_hash.to_string(&TransactionHashBech32Encoder::for_simulator()),
631 "subtxid_sim1kd7eh607wd3wnuq6s290w73jnp6c437580n4q46malda89wz3yvq3cph38",
632 );
633
634 (subintent, actual_subintent_hash)
635 }
636
637 fn create_intent_core_v2(
638 network: &NetworkDefinition,
639 instructions: Vec<InstructionV2>,
640 children: Vec<SubintentHash>,
641 ) -> (IntentCoreV2, Hash) {
642 let (header, expected_header_hash) = create_intent_header_v2(network);
643 let (blobs, expected_blobs_hash) = create_blobs_v1();
644 let (instructions, expected_instructions_hash) =
645 create_subintent_instructions_v2(instructions);
646 let (message, expected_message_hash) = create_message_v2();
647 let (child_intent_constraints, expected_constraints_hash) =
648 create_child_subintent_specifiers_v2(children);
649
650 let intent_core = IntentCoreV2 {
651 header,
652 instructions,
653 blobs,
654 message,
655 children: child_intent_constraints,
656 };
657
658 let expected_hash = hash(
659 [
660 expected_header_hash.as_slice(),
661 expected_blobs_hash.as_slice(),
662 expected_message_hash.as_slice(),
663 expected_constraints_hash.as_slice(),
664 expected_instructions_hash.as_slice(),
665 ]
666 .concat(),
667 );
668 let actual_hash = hash_from_partial_prepare(&intent_core);
669 assert_eq!(expected_hash, actual_hash);
670 (intent_core, expected_hash)
671 }
672
673 fn create_intent_header_v2(network: &NetworkDefinition) -> (IntentHeaderV2, Hash) {
674 let intent_header = IntentHeaderV2 {
675 network_id: network.id,
676 start_epoch_inclusive: Epoch::of(1),
677 end_epoch_exclusive: Epoch::of(10),
678 min_proposer_timestamp_inclusive: None,
679 max_proposer_timestamp_exclusive: Some(Instant::new(0)),
680 intent_discriminator: 0,
681 };
682 let expected_hash = hash_encoded_sbor_value_body(&intent_header);
683 let actual_hash = hash_from_partial_prepare(&intent_header);
684 assert_eq!(expected_hash, actual_hash);
685 (intent_header, expected_hash)
686 }
687
688 fn create_blobs_v1() -> (BlobsV1, Hash) {
689 let blob1: Vec<u8> = vec![0, 1, 2, 3];
690 let blob2: Vec<u8> = vec![5, 6];
691 let expected_hash = hash_contatenated_hashes([hash(&blob1), hash(&blob2)]);
692
693 let blobs_v1 = BlobsV1 {
694 blobs: vec![BlobV1(blob1), BlobV1(blob2)],
695 };
696
697 let actual_hash = hash_from_partial_prepare(&blobs_v1);
698 assert_eq!(expected_hash, actual_hash);
699
700 (blobs_v1, expected_hash)
701 }
702
703 fn create_subintent_instructions_v2(
704 instructions: Vec<InstructionV2>,
705 ) -> (InstructionsV2, Hash) {
706 let instructions = InstructionsV2::from(instructions);
707 let expected_hash = hash_encoded_sbor_value_body(&instructions);
708 let actual_hash = hash_from_partial_prepare(&instructions);
709 assert_eq!(expected_hash, actual_hash);
710
711 (instructions, expected_hash)
712 }
713
714 fn create_message_v2() -> (MessageV2, Hash) {
715 let message = MessageV2::Plaintext(PlaintextMessageV1::text("Hello world!"));
716 let expected_hash = hash_encoded_sbor_value_body(&message);
717
718 let actual_hash = hash_from_partial_prepare(&message);
719 assert_eq!(expected_hash, actual_hash);
720
721 (message, expected_hash)
722 }
723
724 fn create_child_subintent_specifiers_v2(
725 children: Vec<SubintentHash>,
726 ) -> (ChildSubintentSpecifiersV2, Hash) {
727 let child_subintent_specifiers: ChildSubintentSpecifiersV2 = ChildSubintentSpecifiersV2 {
728 children: children.clone().into_iter().map(|h| h.into()).collect(),
729 };
730 let expected_hash = hash_contatenated_hashes(children);
731 let actual_hash = hash_from_partial_prepare(&child_subintent_specifiers);
732 assert_eq!(expected_hash, actual_hash);
733
734 (child_subintent_specifiers, expected_hash)
735 }
736
737 #[test]
740 #[allow(deprecated)] pub fn v1_system_transaction_structure() {
742 let instructions = vec![InstructionV1::DropAuthZoneProofs(DropAuthZoneProofs)];
743 let expected_instructions_hash = hash_encoded_sbor_value(&instructions);
744 let instructions_v1 = InstructionsV1(instructions);
745
746 let blob1: Vec<u8> = vec![0, 1, 2, 3];
747 let blob2: Vec<u8> = vec![5, 6];
748 let expected_blobs_hash =
749 hash([hash(&blob1).0.as_slice(), hash(&blob2).0.as_slice()].concat());
750
751 let blobs_v1 = BlobsV1 {
752 blobs: vec![BlobV1(blob1), BlobV1(blob2)],
753 };
754
755 let prepared_blobs_v1 = blobs_v1
756 .prepare_partial(PreparationSettings::latest_ref())
757 .unwrap();
758 assert_eq!(prepared_blobs_v1.get_summary().hash, expected_blobs_hash);
759
760 let pre_allocated_addresses_v1 = vec![PreAllocatedAddress {
761 blueprint_id: BlueprintId::new(&RESOURCE_PACKAGE, FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT),
762 address: XRD.into(),
763 }];
764 let expected_preallocated_addresses_hash =
765 hash_encoded_sbor_value(&pre_allocated_addresses_v1);
766
767 let hash_for_execution = hash("Pretend genesis transaction");
768
769 let system_transaction_v1 = SystemTransactionV1 {
770 instructions: instructions_v1.clone(),
771 blobs: blobs_v1.clone(),
772 pre_allocated_addresses: pre_allocated_addresses_v1.clone(),
773 hash_for_execution,
774 };
775 let expected_system_transaction_hash = SystemTransactionHash::from_hash(hash(
776 [
777 [
778 TRANSACTION_HASHABLE_PAYLOAD_PREFIX,
779 TransactionDiscriminator::V1System as u8,
780 ]
781 .as_slice(),
782 expected_instructions_hash.0.as_slice(),
783 expected_blobs_hash.0.as_slice(),
784 expected_preallocated_addresses_hash.0.as_slice(),
785 hash_for_execution.0.as_slice(),
786 ]
787 .concat(),
788 ));
789
790 let raw_system_transaction = system_transaction_v1.to_raw().unwrap();
791 SystemTransactionV1::from_raw(&raw_system_transaction)
792 .expect("SystemTransaction can be decoded");
793 let system_transaction_as_versioned =
794 manifest_decode::<AnyTransaction>(raw_system_transaction.as_slice()).unwrap();
795 assert_eq!(
796 system_transaction_as_versioned,
797 AnyTransaction::SystemTransactionV1(system_transaction_v1)
798 );
799
800 let prepared_system_transaction = PreparedSystemTransactionV1::prepare(
801 &raw_system_transaction,
802 PreparationSettings::latest_ref(),
803 )
804 .unwrap();
805
806 assert_eq!(
807 expected_system_transaction_hash,
808 prepared_system_transaction.system_transaction_hash()
809 );
810 assert_eq!(
811 expected_system_transaction_hash
812 .to_string(&TransactionHashBech32Encoder::for_simulator()),
813 "systemtransaction_sim14yf4hrcuqw9y8xrje8jr7h8y3fwnsg9y6nts2f5ru6r8s3yvgguq2da744"
814 );
815 assert_eq!(
816 hex::encode(raw_system_transaction),
817 "4d22040420220112002020020704000102030702050620210102210280000d906318c6318c61e603c64c6318c6318cf7be913d63aafbc6318c6318c60c1746756e6769626c655265736f757263654d616e6167657280005da66318c6318c61f5a61b4c6318c6318cf794aa8d295f14e6318c6318c62007207646fcb3e6a2dbf0fd4830933c54928d3e8dafaf9f704afdae56336fc67aae0d"
818 );
819 }
820}