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 fn hash_encoded_sbor_value<T: ManifestEncode>(value: T) -> Hash {
114 hash(&manifest_encode(&value).unwrap()[1..])
116 }
117
118 fn hash_encoded_sbor_value_body<T: ManifestEncode>(value: T) -> Hash {
119 hash(&manifest_encode(&value).unwrap()[2..])
121 }
122
123 #[test]
126 pub fn v1_user_transaction_structure() {
127 let network = NetworkDefinition::simulator();
128 let preparation_settings = PreparationSettings::babylon();
129
130 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 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 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 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(¬ary_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 #[test]
347 pub fn v2_notarized_transaction_structure() {
348 let network = NetworkDefinition::simulator();
349
350 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 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 #[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}