1use crate::internal_prelude::*;
2
3impl TransactionValidator {
4 pub fn validate_notarized_v2(
5 &self,
6 prepared: PreparedNotarizedTransactionV2,
7 ) -> Result<ValidatedNotarizedTransactionV2, TransactionValidationError> {
8 let ValidatedTransactionTreeV2 {
9 overall_validity_range,
10 total_signature_validations,
11 root_intent_info,
12 root_yield_to_parent_count: _, non_root_subintents_info,
14 } = self.validate_transaction_tree_v2(
15 &prepared,
16 &prepared.signed_intent.transaction_intent.root_intent_core,
17 &prepared
18 .signed_intent
19 .transaction_intent
20 .non_root_subintents,
21 )?;
22
23 Ok(ValidatedNotarizedTransactionV2 {
24 prepared,
25 overall_validity_range,
26 total_signature_validations,
27 transaction_intent_info: root_intent_info,
28 non_root_subintents_info,
29 })
30 }
31
32 pub fn validate_preview_transaction_v2(
33 &self,
34 prepared: PreparedPreviewTransactionV2,
35 ) -> Result<ValidatedPreviewTransactionV2, TransactionValidationError> {
36 let ValidatedTransactionTreeV2 {
37 overall_validity_range,
38 total_signature_validations: total_expected_signature_validations,
39 root_intent_info,
40 root_yield_to_parent_count: _, non_root_subintents_info,
42 } = self.validate_transaction_tree_v2(
43 &prepared,
44 &prepared.transaction_intent.root_intent_core,
45 &prepared.transaction_intent.non_root_subintents,
46 )?;
47
48 Ok(ValidatedPreviewTransactionV2 {
49 prepared,
50 overall_validity_range,
51 total_expected_signature_validations,
52 transaction_intent_info: root_intent_info,
53 non_root_subintents_info,
54 })
55 }
56
57 pub fn validate_transaction_header_v2(
59 &self,
60 header: &TransactionHeaderV2,
61 ) -> Result<(), HeaderValidationError> {
62 if header.tip_basis_points < self.config.min_tip_basis_points
63 || header.tip_basis_points > self.config.max_tip_basis_points
64 {
65 return Err(HeaderValidationError::InvalidTip);
66 }
67
68 Ok(())
69 }
70
71 pub fn validate_signed_partial_transaction_v2(
72 &self,
73 prepared: PreparedSignedPartialTransactionV2,
74 ) -> Result<ValidatedSignedPartialTransactionV2, TransactionValidationError> {
75 let ValidatedTransactionTreeV2 {
76 overall_validity_range,
77 root_intent_info,
78 root_yield_to_parent_count,
79 non_root_subintents_info,
80 total_signature_validations,
81 } = self.validate_transaction_tree_v2(
82 &prepared,
83 &prepared.partial_transaction.root_subintent.intent_core,
84 &prepared.partial_transaction.non_root_subintents,
85 )?;
86
87 Ok(ValidatedSignedPartialTransactionV2 {
88 prepared,
89 total_signature_validations,
90 overall_validity_range,
91 root_subintent_info: root_intent_info,
92 root_subintent_yield_to_parent_count: root_yield_to_parent_count,
93 non_root_subintents_info,
94 })
95 }
96
97 pub fn validate_transaction_tree_v2(
98 &self,
99 signed_intent_tree: &impl SignedIntentTreeStructure,
100 root_intent_core: &PreparedIntentCoreV2,
101 non_root_subintents: &PreparedNonRootSubintentsV2,
102 ) -> Result<ValidatedTransactionTreeV2, TransactionValidationError> {
103 if !self.config.v2_transactions_allowed {
104 return Err(TransactionValidationError::TransactionVersionNotPermitted(
105 2,
106 ));
107 }
108
109 let signatures =
110 signed_intent_tree.construct_pending_signature_validations(&self.config)?;
111
112 let ValidatedIntentTreeInformation {
113 intent_relationships,
114 overall_validity_range,
115 root_yield_summary,
116 } = self.validate_intents_and_structure(signed_intent_tree.intent_tree())?;
117
118 let SignatureValidationSummary {
120 root_signer_keys,
121 non_root_signer_keys,
122 total_signature_validations,
123 } = signatures.validate_all()?;
124
125 let root_intent_info = ValidatedIntentInformationV2 {
126 encoded_instructions: manifest_encode(&root_intent_core.instructions.inner.0)?,
127 children_subintent_indices: intent_relationships.root_intent.children,
128 signer_keys: root_signer_keys,
129 };
130 let non_root_subintents_info = non_root_subintents
131 .subintents
132 .iter()
133 .zip(non_root_signer_keys)
134 .zip(intent_relationships.non_root_subintents.into_values())
135 .map(
136 |((subintent, signer_keys), info)| -> Result<_, TransactionValidationError> {
137 Ok(ValidatedIntentInformationV2 {
138 encoded_instructions: manifest_encode(
139 &subintent.intent_core.instructions.inner.0,
140 )?,
141 signer_keys,
142 children_subintent_indices: info.children,
143 })
144 },
145 )
146 .collect::<Result<_, _>>()?;
147
148 Ok(ValidatedTransactionTreeV2 {
149 overall_validity_range,
150 root_intent_info,
151 root_yield_to_parent_count: root_yield_summary.parent_yields,
152 non_root_subintents_info,
153 total_signature_validations,
154 })
155 }
156
157 pub fn validate_v2_intent_core(
159 &self,
160 intent_core: &PreparedIntentCoreV2,
161 aggregation: &mut AcrossIntentAggregation,
162 is_subintent: bool,
163 ) -> Result<ManifestYieldSummary, IntentValidationError> {
164 self.validate_intent_header_v2(&intent_core.header.inner, aggregation)?;
165 self.validate_message_v2(&intent_core.message.inner)?;
166 aggregation
167 .record_reference_count(intent_core.instructions.references.len(), &self.config)?;
168 let yield_summary = self.validate_manifest_v2(
169 &intent_core.instructions.inner.0,
170 &intent_core.blobs.blobs_by_hash,
171 &intent_core.children.children,
172 is_subintent,
173 )?;
174 Ok(yield_summary)
175 }
176
177 pub fn validate_intent_header_v2(
179 &self,
180 header: &IntentHeaderV2,
181 aggregation: &mut AcrossIntentAggregation,
182 ) -> Result<(), HeaderValidationError> {
183 if let Some(required_network_id) = self.required_network_id {
185 if header.network_id != required_network_id {
186 return Err(HeaderValidationError::InvalidNetwork);
187 }
188 }
189
190 if header.end_epoch_exclusive <= header.start_epoch_inclusive {
192 return Err(HeaderValidationError::InvalidEpochRange);
193 }
194 let max_end_epoch = header
195 .start_epoch_inclusive
196 .after(self.config.max_epoch_range)
197 .ok_or(HeaderValidationError::InvalidEpochRange)?;
198 if header.end_epoch_exclusive > max_end_epoch {
199 return Err(HeaderValidationError::InvalidEpochRange);
200 }
201
202 if let (Some(min_timestamp_inclusive), Some(max_timestamp_exclusive)) = (
203 header.min_proposer_timestamp_inclusive.as_ref(),
204 header.max_proposer_timestamp_exclusive.as_ref(),
205 ) {
206 if min_timestamp_inclusive >= max_timestamp_exclusive {
207 return Err(HeaderValidationError::InvalidTimestampRange);
208 }
209 };
210
211 aggregation.update_headers(
212 header.start_epoch_inclusive,
213 header.end_epoch_exclusive,
214 header.min_proposer_timestamp_inclusive.as_ref(),
215 header.max_proposer_timestamp_exclusive.as_ref(),
216 )?;
217
218 Ok(())
219 }
220
221 pub fn validate_message_v2(&self, message: &MessageV2) -> Result<(), InvalidMessageError> {
223 let validation = &self.config.message_validation;
224 match message {
225 MessageV2::None => {}
226 MessageV2::Plaintext(plaintext_message) => {
227 let PlaintextMessageV1 { mime_type, message } = plaintext_message;
228 if mime_type.len() > validation.max_mime_type_length {
229 return Err(InvalidMessageError::MimeTypeTooLong {
230 actual: mime_type.len(),
231 permitted: validation.max_mime_type_length,
232 });
233 }
234 if message.len() > validation.max_plaintext_message_length {
235 return Err(InvalidMessageError::PlaintextMessageTooLong {
236 actual: message.len(),
237 permitted: validation.max_plaintext_message_length,
238 });
239 }
240 }
241 MessageV2::Encrypted(encrypted_message) => {
242 let EncryptedMessageV2 {
243 encrypted,
244 decryptors_by_curve,
245 } = encrypted_message;
246 if encrypted.0.len() > validation.max_encrypted_message_length {
247 return Err(InvalidMessageError::EncryptedMessageTooLong {
248 actual: encrypted.0.len(),
249 permitted: validation.max_encrypted_message_length,
250 });
251 }
252 if decryptors_by_curve.is_empty() {
253 return Err(InvalidMessageError::NoDecryptors);
254 }
255 let mut total_decryptors = 0;
256 for (curve_type, decryptors) in decryptors_by_curve.iter() {
257 if decryptors.curve_type() != *curve_type {
258 return Err(InvalidMessageError::MismatchingDecryptorCurves {
259 actual: decryptors.curve_type(),
260 expected: *curve_type,
261 });
262 }
263 if decryptors.number_of_decryptors() == 0 {
264 return Err(InvalidMessageError::NoDecryptorsForCurveType {
265 curve_type: decryptors.curve_type(),
266 });
267 }
268 total_decryptors += decryptors.number_of_decryptors();
270 }
271 if total_decryptors > validation.max_decryptors {
272 return Err(InvalidMessageError::TooManyDecryptors {
273 actual: total_decryptors,
274 permitted: validation.max_decryptors,
275 });
276 }
277 }
278 }
279 Ok(())
280 }
281
282 pub fn validate_manifest_v2(
286 &self,
287 instructions: &[InstructionV2],
288 blobs: &IndexMap<Hash, Vec<u8>>,
289 children: &IndexSet<ChildSubintentSpecifier>,
290 is_subintent: bool,
291 ) -> Result<ManifestYieldSummary, ManifestValidationError> {
292 if instructions.len() > self.config.max_instructions {
293 return Err(ManifestValidationError::TooManyInstructions);
294 }
295
296 let mut yield_summary =
297 ManifestYieldSummary::new_with_children(children.iter().map(|c| c.hash));
298 StaticManifestInterpreter::new(
299 ValidationRuleset::cuttlefish(),
300 &EphemeralManifest::new(instructions, blobs, children, is_subintent),
301 )
302 .validate_and_apply_visitor(&mut yield_summary)?;
303
304 Ok(yield_summary)
305 }
306}
307
308impl IntentStructure for PreparedTransactionIntentV2 {
309 fn intent_hash(&self) -> IntentHash {
310 self.transaction_intent_hash().into()
311 }
312
313 fn children(&self) -> impl ExactSizeIterator<Item = SubintentHash> {
314 self.root_intent_core
315 .children
316 .children
317 .iter()
318 .map(|child| child.hash)
319 }
320
321 fn validate_intent(
322 &self,
323 validator: &TransactionValidator,
324 aggregation: &mut AcrossIntentAggregation,
325 ) -> Result<ManifestYieldSummary, IntentValidationError> {
326 validator
327 .validate_transaction_header_v2(&self.transaction_header.inner)
328 .map_err(IntentValidationError::HeaderValidationError)?;
329 validator.validate_v2_intent_core(&self.root_intent_core, aggregation, false)
330 }
331}
332
333impl IntentStructure for PreparedSubintentV2 {
334 fn intent_hash(&self) -> IntentHash {
335 self.subintent_hash().into()
336 }
337
338 fn children(&self) -> impl ExactSizeIterator<Item = SubintentHash> {
339 self.intent_core
340 .children
341 .children
342 .iter()
343 .map(|child| child.hash)
344 }
345
346 fn validate_intent(
347 &self,
348 validator: &TransactionValidator,
349 aggregation: &mut AcrossIntentAggregation,
350 ) -> Result<ManifestYieldSummary, IntentValidationError> {
351 validator.validate_v2_intent_core(&self.intent_core, aggregation, true)
352 }
353}
354
355impl IntentTreeStructure for PreparedTransactionIntentV2 {
356 type RootIntentStructure = Self;
357 type SubintentStructure = PreparedSubintentV2;
358
359 fn root(&self) -> &Self::RootIntentStructure {
360 self
361 }
362
363 fn non_root_subintents(&self) -> impl ExactSizeIterator<Item = &Self::SubintentStructure> {
364 self.non_root_subintents.subintents.iter()
365 }
366}
367
368impl IntentTreeStructure for PreparedPartialTransactionV2 {
369 type RootIntentStructure = PreparedSubintentV2;
370 type SubintentStructure = PreparedSubintentV2;
371
372 fn root(&self) -> &Self::RootIntentStructure {
373 &self.root_subintent
374 }
375
376 fn non_root_subintents(&self) -> impl ExactSizeIterator<Item = &Self::SubintentStructure> {
377 self.non_root_subintents.subintents.iter()
378 }
379}
380
381impl SignedIntentTreeStructure for PreparedNotarizedTransactionV2 {
382 type IntentTree = PreparedTransactionIntentV2;
383
384 fn root_signatures(&self) -> PendingIntentSignatureValidations<'_> {
385 let transaction_intent = &self.signed_intent.transaction_intent;
386 PendingIntentSignatureValidations::TransactionIntent {
387 notary_is_signatory: transaction_intent
388 .transaction_header
389 .inner
390 .notary_is_signatory,
391 notary_public_key: transaction_intent
392 .transaction_header
393 .inner
394 .notary_public_key,
395 notary_signature: self.notary_signature.inner.0,
396 notarized_hash: self.signed_transaction_intent_hash(),
397 intent_signatures: self
398 .signed_intent
399 .transaction_intent_signatures
400 .inner
401 .signatures
402 .as_slice(),
403 signed_hash: transaction_intent.transaction_intent_hash(),
404 }
405 }
406
407 fn non_root_subintent_signatures(
408 &self,
409 ) -> impl ExactSizeIterator<Item = PendingSubintentSignatureValidations<'_>> {
410 self.signed_intent
411 .non_root_subintent_signatures
412 .by_subintent
413 .iter()
414 .map(
415 |signatures| PendingSubintentSignatureValidations::Subintent {
416 intent_signatures: signatures.inner.signatures.as_slice(),
417 },
418 )
419 }
420
421 fn intent_tree(&self) -> &Self::IntentTree {
422 &self.signed_intent.transaction_intent
423 }
424
425 fn transaction_version(&self) -> TransactionVersion {
426 TransactionVersion::V2
427 }
428}
429
430impl SignedIntentTreeStructure for PreparedSignedPartialTransactionV2 {
431 type IntentTree = PreparedPartialTransactionV2;
432
433 fn root_signatures(&self) -> PendingIntentSignatureValidations<'_> {
434 PendingIntentSignatureValidations::Subintent {
435 intent_signatures: self.root_subintent_signatures.inner.signatures.as_slice(),
436 signed_hash: self.intent_tree().subintent_hash(),
437 }
438 }
439
440 fn non_root_subintent_signatures(
441 &self,
442 ) -> impl ExactSizeIterator<Item = PendingSubintentSignatureValidations<'_>> {
443 self.non_root_subintent_signatures
444 .by_subintent
445 .iter()
446 .map(
447 |signatures| PendingSubintentSignatureValidations::Subintent {
448 intent_signatures: signatures.inner.signatures.as_slice(),
449 },
450 )
451 }
452
453 fn intent_tree(&self) -> &Self::IntentTree {
454 &self.partial_transaction
455 }
456
457 fn transaction_version(&self) -> TransactionVersion {
458 TransactionVersion::V2
459 }
460}
461
462impl SignedIntentTreeStructure for PreparedPreviewTransactionV2 {
463 type IntentTree = PreparedTransactionIntentV2;
464
465 fn root_signatures(&self) -> PendingIntentSignatureValidations<'_> {
466 let transaction_intent = &self.transaction_intent;
467 PendingIntentSignatureValidations::PreviewTransactionIntent {
468 notary_is_signatory: transaction_intent
469 .transaction_header
470 .inner
471 .notary_is_signatory,
472 notary_public_key: transaction_intent
473 .transaction_header
474 .inner
475 .notary_public_key,
476 intent_public_keys: self.root_subintent_signatures.inner.as_slice(),
477 }
478 }
479
480 fn non_root_subintent_signatures(
481 &self,
482 ) -> impl ExactSizeIterator<Item = PendingSubintentSignatureValidations<'_>> {
483 self.non_root_subintent_signatures
484 .inner
485 .iter()
486 .map(
487 |public_keys| PendingSubintentSignatureValidations::PreviewSubintent {
488 intent_public_keys: public_keys.as_slice(),
489 },
490 )
491 }
492
493 fn intent_tree(&self) -> &Self::IntentTree {
494 &self.transaction_intent
495 }
496
497 fn transaction_version(&self) -> TransactionVersion {
498 TransactionVersion::V2
499 }
500}
501
502#[cfg(test)]
503mod tests {
504 use crate::internal_prelude::*;
505
506 fn mutate_subintents(
507 transaction: &mut NotarizedTransactionV2,
508 subintents_mutate: impl FnOnce(&mut Vec<SubintentV2>),
509 subintent_signatures_mutate: impl FnOnce(&mut Vec<IntentSignaturesV2>),
510 ) {
511 subintents_mutate(
512 &mut transaction
513 .signed_transaction_intent
514 .transaction_intent
515 .non_root_subintents
516 .0,
517 );
518 subintent_signatures_mutate(
519 &mut transaction
520 .signed_transaction_intent
521 .non_root_subintent_signatures
522 .by_subintent,
523 );
524 }
525
526 #[test]
527 fn test_subintent_structure_errors() {
528 let validator = TransactionValidator::new_for_latest_simulator();
529
530 {
532 let duplicated_subintent = create_leaf_partial_transaction(0, 0);
533 let duplicated_subintent_hash = duplicated_subintent.root_subintent_hash;
534 let mut transaction = TransactionV2Builder::new_with_test_defaults()
535 .add_children([duplicated_subintent])
536 .add_manifest_calling_each_child_once()
537 .default_notarize()
538 .build_minimal_no_validate();
539
540 mutate_subintents(
541 &mut transaction,
542 |subintents| {
543 subintents.push(subintents[0].clone());
544 },
545 |subintent_signatures| {
546 subintent_signatures.push(subintent_signatures[0].clone());
547 },
548 );
549
550 assert_matches!(
551 transaction.prepare_and_validate(&validator),
552 Err(TransactionValidationError::SubintentStructureError(
553 TransactionValidationErrorLocation::NonRootSubintent(SubintentIndex(1), subintent_hash),
554 SubintentStructureError::DuplicateSubintent,
555 )) => {
556 assert_eq!(subintent_hash, duplicated_subintent_hash);
557 }
558 );
559 }
560
561 {
568 let duplicated_subintent = create_leaf_partial_transaction(1, 0);
569
570 let parent_subintent = PartialTransactionV2Builder::new_with_test_defaults()
571 .add_children([duplicated_subintent.clone()])
572 .add_manifest_calling_each_child_once()
573 .build();
574 let mut transaction = TransactionV2Builder::new_with_test_defaults()
575 .add_children([parent_subintent, duplicated_subintent.clone()])
576 .add_manifest_calling_each_child_once()
577 .default_notarize()
578 .build_minimal_no_validate();
579
580 mutate_subintents(
581 &mut transaction,
582 |subintents| {
583 subintents.remove(1);
584 },
585 |subintent_signatures| {
586 subintent_signatures.remove(1);
587 },
588 );
589
590 assert_matches!(
591 transaction.prepare_and_validate(&validator),
592 Err(TransactionValidationError::SubintentStructureError(
593 TransactionValidationErrorLocation::NonRootSubintent(SubintentIndex(1), subintent_hash),
594 SubintentStructureError::SubintentHasMultipleParents,
595 )) => {
596 assert_eq!(subintent_hash, duplicated_subintent.root_subintent_hash);
597 }
598 );
599 }
600
601 {
603 let missing_subintent = create_leaf_partial_transaction(0, 0);
604 let missing_subintent_hash = missing_subintent.root_subintent_hash;
605 let mut transaction = TransactionV2Builder::new_with_test_defaults()
606 .add_children([missing_subintent])
607 .add_manifest_calling_each_child_once()
608 .default_notarize()
609 .build_minimal_no_validate();
610
611 mutate_subintents(
612 &mut transaction,
613 |subintents| {
614 subintents.pop();
615 },
616 |subintent_signatures| {
617 subintent_signatures.pop();
618 },
619 );
620
621 assert_matches!(
622 transaction.prepare_and_validate(&validator),
623 Err(TransactionValidationError::SubintentStructureError(
624 TransactionValidationErrorLocation::Unlocatable,
625 SubintentStructureError::ChildSubintentNotIncludedInTransaction(subintent_hash),
626 )) => {
627 assert_eq!(subintent_hash, missing_subintent_hash);
628 }
629 );
630 }
631
632 {
634 let depth_4 = create_leaf_partial_transaction(0, 0);
635 let depth_4_hash = depth_4.root_subintent_hash;
636 let depth_3 = PartialTransactionV2Builder::new_with_test_defaults()
637 .add_children([depth_4])
638 .add_manifest_calling_each_child_once()
639 .build();
640 let depth_2 = PartialTransactionV2Builder::new_with_test_defaults()
641 .add_children([depth_3])
642 .add_manifest_calling_each_child_once()
643 .build();
644 let depth_1 = PartialTransactionV2Builder::new_with_test_defaults()
645 .add_children([depth_2])
646 .add_manifest_calling_each_child_once()
647 .build();
648 let transaction = TransactionV2Builder::new_with_test_defaults()
649 .add_children([depth_1])
650 .add_manifest_calling_each_child_once()
651 .default_notarize()
652 .build_minimal_no_validate();
653
654 assert_matches!(
655 transaction.prepare_and_validate(&validator),
656 Err(TransactionValidationError::SubintentStructureError(
657 TransactionValidationErrorLocation::NonRootSubintent(SubintentIndex(_), subintent_hash),
658 SubintentStructureError::SubintentExceedsMaxDepth,
659 )) => {
660 assert_eq!(subintent_hash, depth_4_hash);
661 }
662 );
663 }
664
665 {
680 let no_parent_subintent = create_leaf_partial_transaction(0, 0);
682 let no_parent_subintent_hash = no_parent_subintent.root_subintent_hash;
683
684 let mut transaction = TransactionV2Builder::new_with_test_defaults()
685 .add_trivial_manifest()
686 .default_notarize()
687 .build_minimal_no_validate();
688
689 mutate_subintents(
690 &mut transaction,
691 |subintents| {
692 subintents.push(
693 no_parent_subintent
694 .partial_transaction
695 .partial_transaction
696 .root_subintent,
697 );
698 },
699 |subintent_signatures| {
700 subintent_signatures.push(IntentSignaturesV2::none());
701 },
702 );
703
704 assert_matches!(
705 transaction.prepare_and_validate(&validator),
706 Err(TransactionValidationError::SubintentStructureError(
707 TransactionValidationErrorLocation::NonRootSubintent(SubintentIndex(0), subintent_hash),
708 SubintentStructureError::SubintentIsNotReachableFromTheTransactionIntent,
709 )) => {
710 assert_eq!(subintent_hash, no_parent_subintent_hash);
711 }
712 );
713
714 let faked_hash = SubintentHash::from_bytes([1; 32]);
716
717 let self_parent_subintent = SubintentV2 {
718 intent_core: IntentCoreV2 {
719 header: IntentHeaderV2 {
720 network_id: NetworkDefinition::simulator().id,
721 start_epoch_inclusive: Epoch::of(0),
722 end_epoch_exclusive: Epoch::of(1),
723 min_proposer_timestamp_inclusive: None,
724 max_proposer_timestamp_exclusive: None,
725 intent_discriminator: 0,
726 },
727 message: MessageV2::None,
728 instructions: InstructionsV2(vec![InstructionV2::YieldToParent(
729 YieldToParent::empty(),
730 )]),
731 blobs: BlobsV1::none(),
732 children: ChildSubintentSpecifiersV2 {
733 children: indexset![faked_hash.into()],
734 },
735 },
736 };
737
738 let mut transaction = TransactionV2Builder::new_with_test_defaults()
739 .add_trivial_manifest()
740 .default_notarize()
741 .build_minimal_no_validate();
742
743 mutate_subintents(
744 &mut transaction,
745 |subintents| {
746 subintents.push(self_parent_subintent);
747 },
748 |subintent_signatures| {
749 subintent_signatures.push(IntentSignaturesV2::none());
750 },
751 );
752
753 let mut prepared = transaction
754 .prepare(validator.preparation_settings())
755 .unwrap();
756
757 prepared
759 .signed_intent
760 .transaction_intent
761 .non_root_subintents
762 .subintents[0]
763 .summary
764 .hash = faked_hash.0;
765
766 assert_matches!(
767 prepared.validate(&validator),
768 Err(TransactionValidationError::SubintentStructureError(
769 TransactionValidationErrorLocation::NonRootSubintent(SubintentIndex(0), subintent_hash),
770 SubintentStructureError::SubintentIsNotReachableFromTheTransactionIntent,
771 )) => {
772 assert_eq!(subintent_hash, faked_hash);
773 }
774 );
775 }
776
777 {
779 let single_yield_subintent = create_leaf_partial_transaction(0, 0);
780 let single_yield_subintent_hash = single_yield_subintent.root_subintent_hash;
781
782 let transaction = TransactionV2Builder::new_with_test_defaults()
784 .add_signed_child("child", single_yield_subintent.clone())
785 .manifest_builder(|builder| {
786 builder
787 .yield_to_child("child", ())
788 .yield_to_child("child", ())
789 })
790 .default_notarize()
791 .build_minimal_no_validate();
792
793 assert_matches!(
794 transaction.prepare_and_validate(&validator),
795 Err(TransactionValidationError::SubintentStructureError(
796 TransactionValidationErrorLocation::NonRootSubintent(SubintentIndex(0), subintent_hash),
797 SubintentStructureError::MismatchingYieldChildAndYieldParentCountsForSubintent,
798 )) => {
799 assert_eq!(subintent_hash, single_yield_subintent_hash);
800 }
801 );
802
803 let transaction = TransactionV2Builder::new_with_test_defaults()
805 .add_signed_child("child", single_yield_subintent)
806 .manifest_builder(|builder| builder)
807 .default_notarize()
808 .build_minimal_no_validate();
809
810 assert_matches!(
811 transaction.prepare_and_validate(&validator),
812 Err(TransactionValidationError::SubintentStructureError(
813 TransactionValidationErrorLocation::NonRootSubintent(SubintentIndex(0), subintent_hash),
814 SubintentStructureError::MismatchingYieldChildAndYieldParentCountsForSubintent,
815 )) => {
816 assert_eq!(subintent_hash, single_yield_subintent_hash);
817 }
818 );
819
820 let two_parent_yield_subintent = PartialTransactionV2Builder::new_with_test_defaults()
822 .manifest_builder(|builder| builder.yield_to_parent(()).yield_to_parent(()))
823 .build();
824 let two_parent_yield_subintent_hash = two_parent_yield_subintent.root_subintent_hash;
825
826 let three_child_yield_parent = PartialTransactionV2Builder::new_with_test_defaults()
827 .add_signed_child("child", two_parent_yield_subintent)
828 .manifest_builder(|builder| {
829 builder
830 .yield_to_child("child", ())
831 .yield_to_child("child", ())
832 .yield_to_child("child", ())
833 .yield_to_parent(())
834 })
835 .build();
836
837 let transaction = TransactionV2Builder::new_with_test_defaults()
838 .add_children([three_child_yield_parent])
839 .add_manifest_calling_each_child_once()
840 .default_notarize()
841 .build_minimal_no_validate();
842
843 assert_matches!(
844 transaction.prepare_and_validate(&validator),
845 Err(TransactionValidationError::SubintentStructureError(
846 TransactionValidationErrorLocation::NonRootSubintent(SubintentIndex(_), subintent_hash),
847 SubintentStructureError::MismatchingYieldChildAndYieldParentCountsForSubintent,
848 )) => {
849 assert_eq!(subintent_hash, two_parent_yield_subintent_hash);
850 }
851 );
852 }
853 }
854
855 #[test]
857 fn test_valid_messages() {
858 {
860 let message = MessageV2::None;
861 assert_matches!(validate_transaction_with_message(message), Ok(_),);
862 }
863 {
865 let message = MessageV2::Plaintext(PlaintextMessageV1 {
866 mime_type: "text/plain".to_owned(),
867 message: MessageContentsV1::String("Hello world!".to_string()),
868 });
869 assert_matches!(validate_transaction_with_message(message), Ok(_),);
870 }
871 {
873 let message = MessageV2::Encrypted(EncryptedMessageV2 {
876 encrypted: AesGcmPayload(vec![]),
877 decryptors_by_curve: indexmap!(
878 CurveType::Ed25519 => DecryptorsByCurveV2::Ed25519 {
879 dh_ephemeral_public_key: Ed25519PublicKey([0; Ed25519PublicKey::LENGTH]),
880 decryptors: indexmap!(
881 PublicKeyFingerprint([0; PublicKeyFingerprint::LENGTH]) => AesWrapped256BitKey([0; AesWrapped256BitKey::LENGTH]),
882 ),
883 },
884 CurveType::Secp256k1 => DecryptorsByCurveV2::Secp256k1 {
885 dh_ephemeral_public_key: Secp256k1PublicKey([0; Secp256k1PublicKey::LENGTH]),
886 decryptors: indexmap!(
887 PublicKeyFingerprint([0; PublicKeyFingerprint::LENGTH]) => AesWrapped256BitKey([0; AesWrapped256BitKey::LENGTH]),
888 PublicKeyFingerprint([1; PublicKeyFingerprint::LENGTH]) => AesWrapped256BitKey([0; AesWrapped256BitKey::LENGTH]),
889 ),
890 },
891 ),
892 });
893 assert_matches!(validate_transaction_with_message(message), Ok(_),);
894 }
895 }
896
897 #[test]
899 fn test_invalid_message_errors() {
900 {
902 let message = MessageV2::Plaintext(PlaintextMessageV1 {
903 mime_type: "very long mimetype, very long mimetype, very long mimetype, very long mimetype, very long mimetype, very long mimetype, very long mimetype, very long mimetype, ".to_owned(),
904 message: MessageContentsV1::String("Hello".to_string()),
905 });
906 assert_matches!(
907 validate_transaction_with_message(message),
908 Err(InvalidMessageError::MimeTypeTooLong { .. }),
909 );
910 }
911
912 {
914 let mut long_message: String = "".to_owned();
915 while long_message.len() <= 2048 {
916 long_message.push_str("more text please!");
917 }
918 let message = MessageV2::Plaintext(PlaintextMessageV1 {
919 mime_type: "text/plain".to_owned(),
920 message: MessageContentsV1::String(long_message),
921 });
922 assert_matches!(
923 validate_transaction_with_message(message),
924 Err(InvalidMessageError::PlaintextMessageTooLong { .. }),
925 );
926 }
927
928 {
930 let mut message_which_is_too_long: String = "".to_owned();
931 while message_which_is_too_long.len() <= 2048 + 50 {
932 message_which_is_too_long.push_str("more text please!");
934 }
935 let message = MessageV2::Encrypted(EncryptedMessageV2 {
936 encrypted: AesGcmPayload(message_which_is_too_long.as_bytes().to_vec()),
937 decryptors_by_curve: indexmap!(
938 CurveType::Ed25519 => DecryptorsByCurveV2::Ed25519 {
939 dh_ephemeral_public_key: Ed25519PublicKey([0; Ed25519PublicKey::LENGTH]),
940 decryptors: indexmap!(
941 PublicKeyFingerprint([0; PublicKeyFingerprint::LENGTH]) => AesWrapped256BitKey([0; AesWrapped256BitKey::LENGTH]),
942 ),
943 }
944 ),
945 });
946 assert_matches!(
947 validate_transaction_with_message(message),
948 Err(InvalidMessageError::EncryptedMessageTooLong { .. }),
949 );
950 }
951
952 {
954 let message = MessageV2::Encrypted(EncryptedMessageV2 {
955 encrypted: AesGcmPayload(vec![]),
956 decryptors_by_curve: indexmap!(),
957 });
958 assert_matches!(
959 validate_transaction_with_message(message),
960 Err(InvalidMessageError::NoDecryptors),
961 );
962 }
963
964 {
966 let message = MessageV2::Encrypted(EncryptedMessageV2 {
967 encrypted: AesGcmPayload(vec![]),
968 decryptors_by_curve: indexmap!(
969 CurveType::Ed25519 => DecryptorsByCurveV2::Ed25519 {
970 dh_ephemeral_public_key: Ed25519PublicKey([0; Ed25519PublicKey::LENGTH]),
971 decryptors: indexmap!(),
972 }
973 ),
974 });
975 assert_matches!(
976 validate_transaction_with_message(message),
977 Err(InvalidMessageError::NoDecryptorsForCurveType {
978 curve_type: CurveType::Ed25519
979 }),
980 );
981 }
982
983 {
985 let message = MessageV2::Encrypted(EncryptedMessageV2 {
986 encrypted: AesGcmPayload(vec![]),
987 decryptors_by_curve: indexmap!(
988 CurveType::Ed25519 => DecryptorsByCurveV2::Secp256k1 {
989 dh_ephemeral_public_key: Secp256k1PublicKey([0; Secp256k1PublicKey::LENGTH]),
990 decryptors: indexmap!(
991 PublicKeyFingerprint([0; PublicKeyFingerprint::LENGTH]) => AesWrapped256BitKey([0; AesWrapped256BitKey::LENGTH]),
992 ),
993 }
994 ),
995 });
996 assert_matches!(
997 validate_transaction_with_message(message),
998 Err(InvalidMessageError::MismatchingDecryptorCurves {
999 actual: CurveType::Secp256k1,
1000 expected: CurveType::Ed25519
1001 }),
1002 );
1003 }
1004
1005 {
1007 let mut decryptors = IndexMap::<PublicKeyFingerprint, AesWrapped256BitKey>::default();
1008 for i in 0..30 {
1009 decryptors.insert(
1010 PublicKeyFingerprint([0, 0, 0, 0, 0, 0, 0, i as u8]),
1011 AesWrapped256BitKey([0; AesWrapped256BitKey::LENGTH]),
1012 );
1013 }
1014 let message = MessageV2::Encrypted(EncryptedMessageV2 {
1015 encrypted: AesGcmPayload(vec![]),
1016 decryptors_by_curve: indexmap!(
1017 CurveType::Ed25519 => DecryptorsByCurveV2::Ed25519 {
1018 dh_ephemeral_public_key: Ed25519PublicKey([0; Ed25519PublicKey::LENGTH]),
1019 decryptors,
1020 }
1021 ),
1022 });
1023 assert_matches!(
1024 validate_transaction_with_message(message),
1025 Err(InvalidMessageError::TooManyDecryptors {
1026 actual: 30,
1027 permitted: 20
1028 }),
1029 );
1030 }
1031 }
1032
1033 fn validate_transaction_with_message(
1034 message: MessageV2,
1035 ) -> Result<ValidatedNotarizedTransactionV2, InvalidMessageError> {
1036 TransactionV2Builder::new_with_test_defaults()
1037 .add_trivial_manifest()
1038 .message(message)
1039 .default_notarize_and_validate()
1040 .map_err(|e| match e {
1041 TransactionValidationError::IntentValidationError(
1042 _,
1043 IntentValidationError::InvalidMessage(e),
1044 ) => e,
1045 _ => panic!("Expected InvalidMessageError, but got: {:?}", e),
1046 })
1047 }
1048
1049 #[test]
1050 fn too_many_references_should_be_rejected() {
1051 fn create_partial_transaction(
1052 subintent_index: usize,
1053 num_references: usize,
1054 ) -> SignedPartialTransactionV2 {
1055 PartialTransactionV2Builder::new_with_test_defaults()
1056 .intent_discriminator(subintent_index as u64)
1057 .manifest_builder(|mut builder| {
1058 for i in 0..num_references {
1059 let mut address =
1060 [EntityType::GlobalPreallocatedSecp256k1Account as u8; NodeId::LENGTH];
1061 address[1..9].copy_from_slice(
1062 &(((subintent_index + 1) * 1000 + i) as u64).to_le_bytes(),
1063 );
1064 builder = builder.call_method(
1065 ComponentAddress::new_or_panic(address),
1066 "method_name",
1067 (),
1068 );
1069 }
1070
1071 builder.yield_to_parent(())
1072 })
1073 .sign(Secp256k1PrivateKey::from_u64(1000 + subintent_index as u64).unwrap())
1074 .build_minimal()
1075 }
1076
1077 fn validate_transaction(
1078 reference_counts: Vec<usize>,
1079 ) -> Result<ValidatedNotarizedTransactionV2, TransactionValidationError> {
1080 TransactionV2Builder::new_with_test_defaults()
1081 .add_children(
1082 reference_counts
1083 .iter()
1084 .enumerate()
1085 .map(|(i, reference_count)| {
1086 create_partial_transaction(i, *reference_count)
1087 }),
1088 )
1089 .add_manifest_calling_each_child_once()
1090 .sign(Secp256k1PrivateKey::from_u64(1).unwrap())
1091 .default_notarize_and_validate()
1092 }
1093
1094 assert_matches!(validate_transaction(vec![100]), Ok(_));
1095 assert_matches!(
1096 validate_transaction(vec![100, 600]),
1097 Err(TransactionValidationError::IntentValidationError(
1098 TransactionValidationErrorLocation::NonRootSubintent(SubintentIndex(1), _),
1099 IntentValidationError::TooManyReferences {
1100 total: 600,
1101 limit: 512,
1102 }
1103 ))
1104 );
1105 assert_matches!(
1106 validate_transaction(vec![500, 500]),
1107 Err(TransactionValidationError::IntentValidationError(
1108 TransactionValidationErrorLocation::AcrossTransaction,
1109 IntentValidationError::TooManyReferences {
1110 total: 1001, limit: 512,
1112 }
1113 ))
1114 );
1115 }
1116
1117 #[test]
1118 fn test_header_validations() {
1119 let simulator_validator = TransactionValidator::new_for_latest_simulator();
1120 let network_agnostic_validator =
1121 TransactionValidator::new_with_latest_config_network_agnostic();
1122 let config = *simulator_validator.config();
1123
1124 {
1126 let result = TransactionV2Builder::new_with_test_defaults()
1128 .add_trivial_manifest()
1129 .start_epoch_inclusive(Epoch::of(100))
1130 .end_epoch_exclusive(Epoch::of(98))
1131 .default_notarize_and_validate();
1132 assert_matches!(
1133 result,
1134 Err(TransactionValidationError::IntentValidationError(
1135 TransactionValidationErrorLocation::RootTransactionIntent(_),
1136 IntentValidationError::HeaderValidationError(
1137 HeaderValidationError::InvalidEpochRange
1138 ),
1139 )),
1140 );
1141
1142 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1143 .add_trivial_manifest()
1144 .start_epoch_inclusive(Epoch::of(100))
1145 .end_epoch_exclusive(Epoch::of(98))
1146 .build();
1147 let result = TransactionV2Builder::new_with_test_defaults()
1148 .add_children([subintent])
1149 .add_manifest_calling_each_child_once()
1150 .default_notarize_and_validate();
1151 assert_matches!(
1152 result,
1153 Err(TransactionValidationError::IntentValidationError(
1154 TransactionValidationErrorLocation::NonRootSubintent { .. },
1155 IntentValidationError::HeaderValidationError(
1156 HeaderValidationError::InvalidEpochRange
1157 ),
1158 )),
1159 );
1160
1161 let result = TransactionV2Builder::new_with_test_defaults()
1163 .add_trivial_manifest()
1164 .start_epoch_inclusive(Epoch::of(100))
1165 .end_epoch_exclusive(Epoch::of(100))
1166 .default_notarize_and_validate();
1167 assert_matches!(
1168 result,
1169 Err(TransactionValidationError::IntentValidationError(
1170 TransactionValidationErrorLocation::RootTransactionIntent(_),
1171 IntentValidationError::HeaderValidationError(
1172 HeaderValidationError::InvalidEpochRange
1173 ),
1174 )),
1175 );
1176
1177 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1178 .add_trivial_manifest()
1179 .start_epoch_inclusive(Epoch::of(100))
1180 .end_epoch_exclusive(Epoch::of(100))
1181 .build();
1182 let result = TransactionV2Builder::new_with_test_defaults()
1183 .add_children([subintent])
1184 .add_manifest_calling_each_child_once()
1185 .default_notarize_and_validate();
1186 assert_matches!(
1187 result,
1188 Err(TransactionValidationError::IntentValidationError(
1189 TransactionValidationErrorLocation::NonRootSubintent { .. },
1190 IntentValidationError::HeaderValidationError(
1191 HeaderValidationError::InvalidEpochRange
1192 ),
1193 )),
1194 );
1195
1196 let result = TransactionV2Builder::new_with_test_defaults()
1198 .add_trivial_manifest()
1199 .start_epoch_inclusive(Epoch::of(100))
1200 .end_epoch_exclusive(Epoch::of(100 + config.max_epoch_range + 1))
1201 .default_notarize_and_validate();
1202 assert_matches!(
1203 result,
1204 Err(TransactionValidationError::IntentValidationError(
1205 TransactionValidationErrorLocation::RootTransactionIntent(_),
1206 IntentValidationError::HeaderValidationError(
1207 HeaderValidationError::InvalidEpochRange
1208 ),
1209 )),
1210 );
1211
1212 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1213 .add_trivial_manifest()
1214 .start_epoch_inclusive(Epoch::of(100))
1215 .end_epoch_exclusive(Epoch::of(100 + config.max_epoch_range + 1))
1216 .build();
1217 let result = TransactionV2Builder::new_with_test_defaults()
1218 .add_children([subintent])
1219 .add_manifest_calling_each_child_once()
1220 .default_notarize_and_validate();
1221 assert_matches!(
1222 result,
1223 Err(TransactionValidationError::IntentValidationError(
1224 TransactionValidationErrorLocation::NonRootSubintent { .. },
1225 IntentValidationError::HeaderValidationError(
1226 HeaderValidationError::InvalidEpochRange
1227 ),
1228 )),
1229 );
1230 }
1231
1232 {
1234 let result = TransactionV2Builder::new_with_test_defaults()
1236 .add_trivial_manifest()
1237 .min_proposer_timestamp_inclusive(Some(Instant::new(5000)))
1238 .max_proposer_timestamp_exclusive(Some(Instant::new(4999)))
1239 .end_epoch_exclusive(Epoch::of(98))
1240 .default_notarize_and_validate();
1241 assert_matches!(
1242 result,
1243 Err(TransactionValidationError::IntentValidationError(
1244 TransactionValidationErrorLocation::RootTransactionIntent(_),
1245 IntentValidationError::HeaderValidationError(
1246 HeaderValidationError::InvalidTimestampRange
1247 ),
1248 )),
1249 );
1250
1251 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1252 .add_trivial_manifest()
1253 .min_proposer_timestamp_inclusive(Some(Instant::new(5000)))
1254 .max_proposer_timestamp_exclusive(Some(Instant::new(4999)))
1255 .build();
1256 let result = TransactionV2Builder::new_with_test_defaults()
1257 .add_children([subintent])
1258 .add_manifest_calling_each_child_once()
1259 .default_notarize_and_validate();
1260 assert_matches!(
1261 result,
1262 Err(TransactionValidationError::IntentValidationError(
1263 TransactionValidationErrorLocation::NonRootSubintent { .. },
1264 IntentValidationError::HeaderValidationError(
1265 HeaderValidationError::InvalidTimestampRange
1266 ),
1267 )),
1268 );
1269
1270 let result = TransactionV2Builder::new_with_test_defaults()
1272 .add_trivial_manifest()
1273 .min_proposer_timestamp_inclusive(Some(Instant::new(5000)))
1274 .max_proposer_timestamp_exclusive(Some(Instant::new(5000)))
1275 .default_notarize_and_validate();
1276 assert_matches!(
1277 result,
1278 Err(TransactionValidationError::IntentValidationError(
1279 TransactionValidationErrorLocation::RootTransactionIntent(_),
1280 IntentValidationError::HeaderValidationError(
1281 HeaderValidationError::InvalidTimestampRange
1282 ),
1283 )),
1284 );
1285
1286 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1287 .add_trivial_manifest()
1288 .min_proposer_timestamp_inclusive(Some(Instant::new(5000)))
1289 .max_proposer_timestamp_exclusive(Some(Instant::new(5000)))
1290 .build();
1291 let result = TransactionV2Builder::new_with_test_defaults()
1292 .add_children([subintent])
1293 .add_manifest_calling_each_child_once()
1294 .default_notarize_and_validate();
1295 assert_matches!(
1296 result,
1297 Err(TransactionValidationError::IntentValidationError(
1298 TransactionValidationErrorLocation::NonRootSubintent { .. },
1299 IntentValidationError::HeaderValidationError(
1300 HeaderValidationError::InvalidTimestampRange
1301 ),
1302 )),
1303 );
1304
1305 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1307 .add_trivial_manifest()
1308 .min_proposer_timestamp_inclusive(Some(Instant::new(5000)))
1309 .max_proposer_timestamp_exclusive(Some(Instant::new(5001)))
1310 .build();
1311 let result = TransactionV2Builder::new_with_test_defaults()
1312 .add_children([subintent])
1313 .add_manifest_calling_each_child_once()
1314 .default_notarize_and_validate();
1315 assert_matches!(result, Ok(_),);
1316 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1317 .add_trivial_manifest()
1318 .min_proposer_timestamp_inclusive(Some(Instant::new(5000)))
1319 .max_proposer_timestamp_exclusive(None)
1320 .build();
1321 let result = TransactionV2Builder::new_with_test_defaults()
1322 .add_children([subintent])
1323 .add_manifest_calling_each_child_once()
1324 .default_notarize_and_validate();
1325 assert_matches!(result, Ok(_),);
1326 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1327 .add_trivial_manifest()
1328 .min_proposer_timestamp_inclusive(None)
1329 .max_proposer_timestamp_exclusive(Some(Instant::new(5000)))
1330 .build();
1331 let result = TransactionV2Builder::new_with_test_defaults()
1332 .add_children([subintent])
1333 .add_manifest_calling_each_child_once()
1334 .default_notarize_and_validate();
1335 assert_matches!(result, Ok(_),);
1336 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1337 .add_trivial_manifest()
1338 .min_proposer_timestamp_inclusive(None)
1339 .max_proposer_timestamp_exclusive(None)
1340 .build();
1341 let result = TransactionV2Builder::new_with_test_defaults()
1342 .add_children([subintent])
1343 .add_manifest_calling_each_child_once()
1344 .default_notarize_and_validate();
1345 assert_matches!(result, Ok(_),);
1346 }
1347
1348 {
1350 let result = TransactionV2Builder::new_with_test_defaults()
1351 .add_trivial_manifest()
1352 .network_id(NetworkDefinition::mainnet().id)
1353 .default_notarize()
1354 .build_minimal_no_validate()
1355 .prepare_and_validate(&simulator_validator);
1356 assert_matches!(
1357 result,
1358 Err(TransactionValidationError::IntentValidationError(
1359 TransactionValidationErrorLocation::RootTransactionIntent(_),
1360 IntentValidationError::HeaderValidationError(
1361 HeaderValidationError::InvalidNetwork
1362 ),
1363 )),
1364 );
1365
1366 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1367 .add_trivial_manifest()
1368 .network_id(NetworkDefinition::mainnet().id)
1369 .build();
1370 let result = TransactionV2Builder::new_with_test_defaults()
1371 .add_children([subintent])
1372 .add_manifest_calling_each_child_once()
1373 .default_notarize()
1374 .build_minimal_no_validate()
1375 .prepare_and_validate(&simulator_validator);
1376 assert_matches!(
1377 result,
1378 Err(TransactionValidationError::IntentValidationError(
1379 TransactionValidationErrorLocation::NonRootSubintent { .. },
1380 IntentValidationError::HeaderValidationError(
1381 HeaderValidationError::InvalidNetwork
1382 ),
1383 )),
1384 );
1385
1386 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1390 .add_trivial_manifest()
1391 .network_id(NetworkDefinition::mainnet().id)
1392 .build();
1393 let result = TransactionV2Builder::new_with_test_defaults()
1394 .add_children([subintent])
1395 .add_manifest_calling_each_child_once()
1396 .default_notarize()
1397 .build_minimal_no_validate()
1398 .prepare_and_validate(&network_agnostic_validator);
1399 assert_matches!(result, Ok(_),);
1400 }
1401
1402 {
1404 let result = TransactionV2Builder::new_with_test_defaults()
1406 .add_trivial_manifest()
1407 .tip_basis_points(config.max_tip_basis_points + 1)
1408 .default_notarize_and_validate();
1409 assert_matches!(
1410 result,
1411 Err(TransactionValidationError::IntentValidationError(
1412 TransactionValidationErrorLocation::RootTransactionIntent(_),
1413 IntentValidationError::HeaderValidationError(HeaderValidationError::InvalidTip),
1414 )),
1415 );
1416 }
1417
1418 {
1420 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1422 .add_trivial_manifest()
1423 .start_epoch_inclusive(Epoch::of(100))
1424 .end_epoch_exclusive(Epoch::of(102))
1425 .build();
1426 let result = TransactionV2Builder::new_with_test_defaults()
1427 .add_children([subintent])
1428 .start_epoch_inclusive(Epoch::of(102))
1429 .end_epoch_exclusive(Epoch::of(103))
1430 .add_manifest_calling_each_child_once()
1431 .default_notarize_and_validate();
1432 assert_matches!(
1433 result,
1434 Err(TransactionValidationError::IntentValidationError(
1435 TransactionValidationErrorLocation::NonRootSubintent { .. },
1436 IntentValidationError::HeaderValidationError(
1437 HeaderValidationError::NoValidEpochRangeAcrossAllIntents
1438 ),
1439 )),
1440 );
1441
1442 let subintent_1 = PartialTransactionV2Builder::new_with_test_defaults()
1444 .add_trivial_manifest()
1445 .start_epoch_inclusive(Epoch::of(100))
1446 .end_epoch_exclusive(Epoch::of(102))
1447 .build();
1448 let subintent_2 = PartialTransactionV2Builder::new_with_test_defaults()
1449 .add_trivial_manifest()
1450 .start_epoch_inclusive(Epoch::of(101))
1451 .end_epoch_exclusive(Epoch::of(103))
1452 .build();
1453 let subintent_3 = PartialTransactionV2Builder::new_with_test_defaults()
1454 .add_trivial_manifest()
1455 .start_epoch_inclusive(Epoch::of(102))
1456 .end_epoch_exclusive(Epoch::of(104))
1457 .build();
1458 let result = TransactionV2Builder::new_with_test_defaults()
1459 .add_children([subintent_1, subintent_2, subintent_3])
1460 .start_epoch_inclusive(Epoch::of(100))
1461 .end_epoch_exclusive(Epoch::of(105))
1462 .add_manifest_calling_each_child_once()
1463 .default_notarize_and_validate();
1464 assert_matches!(
1465 result,
1466 Err(TransactionValidationError::IntentValidationError(
1467 TransactionValidationErrorLocation::NonRootSubintent { .. },
1468 IntentValidationError::HeaderValidationError(
1469 HeaderValidationError::NoValidEpochRangeAcrossAllIntents
1470 ),
1471 )),
1472 );
1473
1474 let subintent_1 = PartialTransactionV2Builder::new_with_test_defaults()
1476 .add_trivial_manifest()
1477 .start_epoch_inclusive(Epoch::of(100))
1478 .end_epoch_exclusive(Epoch::of(102))
1479 .build();
1480 let subintent_2 = PartialTransactionV2Builder::new_with_test_defaults()
1481 .add_trivial_manifest()
1482 .start_epoch_inclusive(Epoch::of(101))
1483 .end_epoch_exclusive(Epoch::of(103))
1484 .build();
1485 let subintent_3 = PartialTransactionV2Builder::new_with_test_defaults()
1486 .add_trivial_manifest()
1487 .start_epoch_inclusive(Epoch::of(101))
1488 .end_epoch_exclusive(Epoch::of(104))
1489 .build();
1490 let result = TransactionV2Builder::new_with_test_defaults()
1491 .add_children([subintent_1, subintent_2, subintent_3])
1492 .start_epoch_inclusive(Epoch::of(100))
1493 .end_epoch_exclusive(Epoch::of(105))
1494 .add_manifest_calling_each_child_once()
1495 .default_notarize_and_validate();
1496 assert_matches!(
1497 result,
1498 Ok(validated) => {
1499 assert_eq!(validated.overall_validity_range.epoch_range.start_epoch_inclusive, Epoch::of(101));
1500 assert_eq!(validated.overall_validity_range.epoch_range.end_epoch_exclusive, Epoch::of(102));
1501 },
1502 );
1503 }
1504
1505 {
1507 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1509 .add_trivial_manifest()
1510 .min_proposer_timestamp_inclusive(Some(Instant::new(5000)))
1511 .build();
1512 let result = TransactionV2Builder::new_with_test_defaults()
1513 .add_children([subintent])
1514 .max_proposer_timestamp_exclusive(Some(Instant::new(5000)))
1515 .add_manifest_calling_each_child_once()
1516 .default_notarize_and_validate();
1517 assert_matches!(
1518 result,
1519 Err(TransactionValidationError::IntentValidationError(
1520 TransactionValidationErrorLocation::NonRootSubintent { .. },
1521 IntentValidationError::HeaderValidationError(
1522 HeaderValidationError::NoValidTimestampRangeAcrossAllIntents
1523 ),
1524 )),
1525 );
1526
1527 let subintent_1 = PartialTransactionV2Builder::new_with_test_defaults()
1529 .add_trivial_manifest()
1530 .max_proposer_timestamp_exclusive(Some(Instant::new(4000)))
1531 .build();
1532 let subintent_2 = PartialTransactionV2Builder::new_with_test_defaults()
1533 .add_trivial_manifest()
1534 .max_proposer_timestamp_exclusive(Some(Instant::new(4003)))
1535 .build();
1536 let subintent_3 = PartialTransactionV2Builder::new_with_test_defaults()
1537 .add_trivial_manifest()
1538 .min_proposer_timestamp_inclusive(Some(Instant::new(4001)))
1539 .build();
1540 let result = TransactionV2Builder::new_with_test_defaults()
1541 .add_children([subintent_1, subintent_2, subintent_3])
1542 .add_manifest_calling_each_child_once()
1543 .default_notarize_and_validate();
1544 assert_matches!(
1545 result,
1546 Err(TransactionValidationError::IntentValidationError(
1547 TransactionValidationErrorLocation::NonRootSubintent { .. },
1548 IntentValidationError::HeaderValidationError(
1549 HeaderValidationError::NoValidTimestampRangeAcrossAllIntents
1550 ),
1551 )),
1552 );
1553
1554 let subintent_1 = PartialTransactionV2Builder::new_with_test_defaults()
1556 .add_trivial_manifest()
1557 .max_proposer_timestamp_exclusive(Some(Instant::new(4003)))
1558 .build();
1559 let subintent_2 = PartialTransactionV2Builder::new_with_test_defaults()
1560 .add_trivial_manifest()
1561 .max_proposer_timestamp_exclusive(Some(Instant::new(4005)))
1562 .build();
1563 let subintent_3 = PartialTransactionV2Builder::new_with_test_defaults()
1564 .add_trivial_manifest()
1565 .min_proposer_timestamp_inclusive(Some(Instant::new(3998)))
1566 .max_proposer_timestamp_exclusive(Some(Instant::new(4001)))
1567 .build();
1568 let result = TransactionV2Builder::new_with_test_defaults()
1569 .add_children([subintent_1, subintent_2, subintent_3])
1570 .min_proposer_timestamp_inclusive(Some(Instant::new(3999)))
1571 .max_proposer_timestamp_exclusive(Some(Instant::new(5999)))
1572 .add_manifest_calling_each_child_once()
1573 .default_notarize_and_validate();
1574 assert_matches!(
1575 result,
1576 Ok(validated) => {
1577 assert_eq!(validated.overall_validity_range.proposer_timestamp_range.start_timestamp_inclusive, Some(Instant::new(3999)));
1578 assert_eq!(validated.overall_validity_range.proposer_timestamp_range.end_timestamp_exclusive, Some(Instant::new(4001)));
1579 },
1580 );
1581 }
1582 }
1583
1584 trait ManifestBuilderExtensions {
1585 fn add_test_method_call_with(self, value: impl ManifestEncode) -> Self;
1586 }
1587
1588 impl<M: BuildableManifest> ManifestBuilderExtensions for ManifestBuilder<M>
1589 where
1590 CallMethod: Into<M::Instruction>,
1591 {
1592 fn add_test_method_call_with(self, value: impl ManifestEncode) -> Self {
1593 self.add_raw_instruction_ignoring_all_side_effects(CallMethod {
1594 address: XRD.into(),
1595 method_name: "method".into(),
1596 args: manifest_decode(&manifest_encode(&(value,)).unwrap()).unwrap(),
1597 })
1598 }
1599 }
1600
1601 #[test]
1602 fn test_manifest_validations() {
1603 let account_address = ComponentAddress::preallocated_account_from_public_key(
1604 &Ed25519PublicKey([0; Ed25519PublicKey::LENGTH]),
1605 );
1606
1607 fn validate_transaction_manifest(
1608 manifest: TransactionManifestV2,
1609 ) -> Result<ValidatedNotarizedTransactionV2, ManifestValidationError> {
1610 let builder = TransactionV2Builder::new_with_test_defaults().manifest(manifest);
1611 validate_transaction_builder_manifest(builder)
1612 }
1613
1614 fn validate_transaction_builder_manifest(
1615 builder: TransactionV2Builder,
1616 ) -> Result<ValidatedNotarizedTransactionV2, ManifestValidationError> {
1617 builder
1618 .default_notarize_and_validate()
1619 .map_err(|err| match err {
1620 TransactionValidationError::IntentValidationError(
1621 _,
1622 IntentValidationError::ManifestValidationError(err),
1623 ) => err,
1624 _ => panic!("Expected ManifestValidationError, but got: {:?}", err),
1625 })
1626 }
1627
1628 fn validate_subintent_manifest(
1629 subintent_manifest: SubintentManifestV2,
1630 ) -> Result<ValidatedNotarizedTransactionV2, ManifestValidationError> {
1631 let subintent = PartialTransactionV2Builder::new_with_test_defaults()
1632 .manifest(subintent_manifest)
1633 .build();
1634 TransactionV2Builder::new_with_test_defaults()
1635 .add_children([subintent])
1636 .add_manifest_calling_each_child_once()
1637 .default_notarize_and_validate()
1638 .map_err(|err| match err {
1639 TransactionValidationError::IntentValidationError(
1640 _,
1641 IntentValidationError::ManifestValidationError(err),
1642 ) => err,
1643 _ => panic!("Expected ManifestValidationError, but got: {:?}", err),
1644 })
1645 }
1646
1647 {
1649 }
1652
1653 {
1655 let transaction_manifest = ManifestBuilder::new_v2()
1656 .add_test_method_call_with(ManifestBlobRef([2; 32]))
1657 .build_no_validate();
1658
1659 assert_matches!(
1660 validate_transaction_manifest(transaction_manifest),
1661 Err(ManifestValidationError::BlobNotRegistered(ManifestBlobRef(blob_ref))) => {
1662 assert_eq!(blob_ref, [2; 32]);
1663 }
1664 );
1665
1666 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1667 .add_test_method_call_with(ManifestBlobRef([3; 32]))
1668 .yield_to_parent(())
1669 .build_no_validate();
1670 assert_matches!(
1671 validate_subintent_manifest(subintent_manifest),
1672 Err(ManifestValidationError::BlobNotRegistered(ManifestBlobRef(blob_ref))) => {
1673 assert_eq!(blob_ref, [3; 32]);
1674 }
1675 );
1676 }
1677
1678 {
1680 let transaction_manifest = ManifestBuilder::new_v2()
1681 .add_test_method_call_with(ManifestBucket(2))
1682 .build_no_validate();
1683
1684 assert_matches!(
1685 validate_transaction_manifest(transaction_manifest),
1686 Err(ManifestValidationError::BucketNotYetCreated(bucket)) => {
1687 assert_eq!(bucket, ManifestBucket(2));
1688 },
1689 );
1690
1691 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1692 .add_test_method_call_with(ManifestBucket(3))
1693 .yield_to_parent(())
1694 .build_no_validate();
1695 assert_matches!(
1696 validate_subintent_manifest(subintent_manifest),
1697 Err(ManifestValidationError::BucketNotYetCreated(bucket)) => {
1698 assert_eq!(bucket, ManifestBucket(3));
1699 }
1700 );
1701 }
1702
1703 {
1705 let transaction_manifest = ManifestBuilder::new_v2()
1706 .take_all_from_worktop(XRD, "reused_bucket")
1707 .add_test_method_call_with(ManifestBucket(0))
1708 .add_test_method_call_with(ManifestBucket(0))
1709 .build_no_validate();
1710
1711 assert_matches!(
1712 validate_transaction_manifest(transaction_manifest),
1713 Err(ManifestValidationError::BucketAlreadyUsed(bucket, _)) => {
1714 assert_eq!(bucket, ManifestBucket(0));
1715 },
1716 );
1717
1718 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1719 .take_all_from_worktop(XRD, "reused_bucket")
1720 .add_test_method_call_with(ManifestBucket(0))
1721 .add_test_method_call_with(ManifestBucket(0))
1722 .yield_to_parent(())
1723 .build_no_validate();
1724 assert_matches!(
1725 validate_subintent_manifest(subintent_manifest),
1726 Err(ManifestValidationError::BucketAlreadyUsed(bucket, _)) => {
1727 assert_eq!(bucket, ManifestBucket(0));
1728 },
1729 );
1730 }
1731
1732 {
1734 let transaction_manifest = ManifestBuilder::new_v2()
1735 .take_all_from_worktop(XRD, "my_bucket")
1736 .create_proof_from_bucket_of_all("my_bucket", "my_proof")
1737 .deposit(account_address, "my_bucket")
1738 .build_no_validate();
1739
1740 assert_matches!(
1741 validate_transaction_manifest(transaction_manifest),
1742 Err(ManifestValidationError::BucketConsumedWhilstLockedByProof(bucket, _)) => {
1743 assert_eq!(bucket, ManifestBucket(0));
1744 },
1745 );
1746
1747 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1748 .take_all_from_worktop(XRD, "my_bucket")
1749 .create_proof_from_bucket_of_all("my_bucket", "my_proof")
1750 .deposit(account_address, "my_bucket")
1751 .yield_to_parent(())
1752 .build_no_validate();
1753 assert_matches!(
1754 validate_subintent_manifest(subintent_manifest),
1755 Err(ManifestValidationError::BucketConsumedWhilstLockedByProof(bucket, _)) => {
1756 assert_eq!(bucket, ManifestBucket(0));
1757 },
1758 );
1759 }
1760
1761 {
1763 let transaction_manifest = ManifestBuilder::new_v2()
1764 .add_test_method_call_with(ManifestProof(2))
1765 .build_no_validate();
1766
1767 assert_matches!(
1768 validate_transaction_manifest(transaction_manifest),
1769 Err(ManifestValidationError::ProofNotYetCreated(proof)) => {
1770 assert_eq!(proof, ManifestProof(2));
1771 },
1772 );
1773
1774 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1775 .add_test_method_call_with(ManifestProof(2))
1776 .yield_to_parent(())
1777 .build_no_validate();
1778 assert_matches!(
1779 validate_subintent_manifest(subintent_manifest),
1780 Err(ManifestValidationError::ProofNotYetCreated(proof)) => {
1781 assert_eq!(proof, ManifestProof(2));
1782 },
1783 );
1784 }
1785
1786 {
1788 let transaction_manifest = ManifestBuilder::new_v2()
1789 .create_proof_from_auth_zone_of_all(XRD, "proof")
1790 .add_test_method_call_with(ManifestProof(0))
1791 .add_test_method_call_with(ManifestProof(0))
1792 .build_no_validate();
1793
1794 assert_matches!(
1795 validate_transaction_manifest(transaction_manifest),
1796 Err(ManifestValidationError::ProofAlreadyUsed(proof, _)) => {
1797 assert_eq!(proof, ManifestProof(0));
1798 },
1799 );
1800
1801 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1802 .create_proof_from_auth_zone_of_all(XRD, "proof")
1803 .add_test_method_call_with(ManifestProof(0))
1804 .add_test_method_call_with(ManifestProof(0))
1805 .yield_to_parent(())
1806 .build_no_validate();
1807 assert_matches!(
1808 validate_subintent_manifest(subintent_manifest),
1809 Err(ManifestValidationError::ProofAlreadyUsed(proof, _)) => {
1810 assert_eq!(proof, ManifestProof(0));
1811 },
1812 );
1813 }
1814
1815 {
1817 let transaction_manifest = ManifestBuilder::new_v2()
1818 .add_test_method_call_with(ManifestAddressReservation(2))
1819 .build_no_validate();
1820
1821 assert_matches!(
1822 validate_transaction_manifest(transaction_manifest),
1823 Err(ManifestValidationError::AddressReservationNotYetCreated(reservation)) => {
1824 assert_eq!(reservation, ManifestAddressReservation(2));
1825 },
1826 );
1827
1828 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1829 .add_test_method_call_with(ManifestAddressReservation(2))
1830 .yield_to_parent(())
1831 .build_no_validate();
1832 assert_matches!(
1833 validate_subintent_manifest(subintent_manifest),
1834 Err(ManifestValidationError::AddressReservationNotYetCreated(reservation)) => {
1835 assert_eq!(reservation, ManifestAddressReservation(2));
1836 },
1837 );
1838 }
1839
1840 {
1842 let transaction_manifest = ManifestBuilder::new_v2()
1843 .allocate_global_address(
1844 RESOURCE_PACKAGE,
1845 FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
1846 "my_address_reservation",
1847 "my_address",
1848 )
1849 .add_test_method_call_with(ManifestAddressReservation(0))
1850 .add_test_method_call_with(ManifestAddressReservation(0))
1851 .build_no_validate();
1852
1853 assert_matches!(
1854 validate_transaction_manifest(transaction_manifest),
1855 Err(ManifestValidationError::AddressReservationAlreadyUsed(reservation, _)) => {
1856 assert_eq!(reservation, ManifestAddressReservation(0));
1857 },
1858 );
1859
1860 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1861 .allocate_global_address(
1862 RESOURCE_PACKAGE,
1863 FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
1864 "my_address_reservation",
1865 "my_address",
1866 )
1867 .add_test_method_call_with(ManifestAddressReservation(0))
1868 .add_test_method_call_with(ManifestAddressReservation(0))
1869 .yield_to_parent(())
1870 .build_no_validate();
1871 assert_matches!(
1872 validate_subintent_manifest(subintent_manifest),
1873 Err(ManifestValidationError::AddressReservationAlreadyUsed(reservation, _)) => {
1874 assert_eq!(reservation, ManifestAddressReservation(0));
1875 },
1876 );
1877 }
1878
1879 {
1881 let transaction_manifest = ManifestBuilder::new_v2()
1882 .add_test_method_call_with(ManifestAddress::Named(ManifestNamedAddress(2)))
1883 .build_no_validate();
1884
1885 assert_matches!(
1886 validate_transaction_manifest(transaction_manifest),
1887 Err(ManifestValidationError::NamedAddressNotYetCreated(named_address)) => {
1888 assert_eq!(named_address, ManifestNamedAddress(2));
1889 },
1890 );
1891
1892 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1893 .add_test_method_call_with(ManifestAddress::Named(ManifestNamedAddress(2)))
1894 .yield_to_parent(())
1895 .build_no_validate();
1896 assert_matches!(
1897 validate_subintent_manifest(subintent_manifest),
1898 Err(ManifestValidationError::NamedAddressNotYetCreated(named_address)) => {
1899 assert_eq!(named_address, ManifestNamedAddress(2));
1900 },
1901 );
1902 }
1903
1904 {
1906 let transaction_manifest = ManifestBuilder::new_v2()
1907 .add_raw_instruction_ignoring_all_side_effects(YieldToChild {
1908 child_index: ManifestNamedIntentIndex(2),
1909 args: ManifestValue::unit(),
1910 })
1911 .build_no_validate();
1912
1913 assert_matches!(
1914 validate_transaction_manifest(transaction_manifest),
1915 Err(ManifestValidationError::ChildIntentNotRegistered(named_intent)) => {
1916 assert_eq!(named_intent, ManifestNamedIntent(2));
1917 },
1918 );
1919
1920 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1921 .add_raw_instruction_ignoring_all_side_effects(YieldToChild {
1922 child_index: ManifestNamedIntentIndex(3),
1923 args: ManifestValue::unit(),
1924 })
1925 .yield_to_parent(())
1926 .build_no_validate();
1927 assert_matches!(
1928 validate_subintent_manifest(subintent_manifest),
1929 Err(ManifestValidationError::ChildIntentNotRegistered(named_intent)) => {
1930 assert_eq!(named_intent, ManifestNamedIntent(3));
1931 },
1932 );
1933 }
1934
1935 {
1937 let transaction_manifest = ManifestBuilder::new_v2()
1938 .take_all_from_worktop(XRD, "my_bucket")
1939 .build_no_validate();
1940
1941 assert_matches!(
1942 validate_transaction_manifest(transaction_manifest),
1943 Err(ManifestValidationError::DanglingBucket(bucket, _)) => {
1944 assert_eq!(bucket, ManifestBucket(0));
1945 },
1946 );
1947
1948 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1949 .take_all_from_worktop(XRD, "my_bucket")
1950 .yield_to_parent(())
1951 .build_no_validate();
1952 assert_matches!(
1953 validate_subintent_manifest(subintent_manifest),
1954 Err(ManifestValidationError::DanglingBucket(bucket, _)) => {
1955 assert_eq!(bucket, ManifestBucket(0));
1956 },
1957 );
1958 }
1959
1960 {
1962 let transaction_manifest = ManifestBuilder::new_v2()
1963 .allocate_global_address(
1964 RESOURCE_PACKAGE,
1965 FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
1966 "my_address_reservation",
1967 "my_address",
1968 )
1969 .build_no_validate();
1970
1971 assert_matches!(
1972 validate_transaction_manifest(transaction_manifest),
1973 Err(ManifestValidationError::DanglingAddressReservation(reservation, _)) => {
1974 assert_eq!(reservation, ManifestAddressReservation(0));
1975 },
1976 );
1977
1978 let subintent_manifest = ManifestBuilder::new_subintent_v2()
1979 .allocate_global_address(
1980 RESOURCE_PACKAGE,
1981 FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT,
1982 "my_address_reservation",
1983 "my_address",
1984 )
1985 .yield_to_parent(())
1986 .build_no_validate();
1987 assert_matches!(
1988 validate_subintent_manifest(subintent_manifest),
1989 Err(ManifestValidationError::DanglingAddressReservation(reservation, _)) => {
1990 assert_eq!(reservation, ManifestAddressReservation(0));
1991 },
1992 );
1993 }
1994
1995 {
1997 }
2000
2001 {
2003 }
2006
2007 {
2009 let transaction_manifest = ManifestBuilder::new_v2()
2011 .add_raw_instruction_ignoring_all_side_effects(YieldToParent {
2012 args: ManifestValue::unit(),
2013 })
2014 .build_no_validate();
2015
2016 assert_matches!(
2017 validate_transaction_manifest(transaction_manifest),
2018 Err(ManifestValidationError::InstructionNotSupportedInTransactionIntent),
2019 );
2020
2021 let transaction_manifest = ManifestBuilder::new_v2()
2023 .add_raw_instruction_ignoring_all_side_effects(VerifyParent {
2024 access_rule: rule!(allow_all),
2025 })
2026 .build_no_validate();
2027 assert_matches!(
2028 validate_transaction_manifest(transaction_manifest),
2029 Err(ManifestValidationError::InstructionNotSupportedInTransactionIntent),
2030 );
2031 }
2032
2033 {
2035 let subintent_manifest = ManifestBuilder::new_subintent_v2()
2037 .add_test_method_call_with(())
2038 .build_no_validate();
2039
2040 assert_matches!(
2041 validate_subintent_manifest(subintent_manifest),
2042 Err(ManifestValidationError::SubintentDoesNotEndWithYieldToParent),
2043 );
2044
2045 let subintent_manifest = ManifestBuilder::new_subintent_v2().build_no_validate();
2047
2048 assert_matches!(
2049 validate_subintent_manifest(subintent_manifest),
2050 Err(ManifestValidationError::SubintentDoesNotEndWithYieldToParent),
2051 );
2052 }
2053
2054 {
2056 let subintent = create_leaf_partial_transaction(0, 0);
2057 let builder = ManifestBuilder::new_v2();
2058 let lookup = builder.name_lookup();
2059 let transaction_manifest = builder
2060 .use_child("child_1", subintent.root_subintent_hash)
2061 .create_proof_from_auth_zone_of_all(XRD, "my_proof")
2062 .yield_to_child("child_1", (lookup.proof("my_proof"),))
2063 .build_no_validate();
2064 let builder = TransactionV2Builder::new_with_test_defaults()
2065 .add_signed_child("child_1", subintent)
2066 .manifest(transaction_manifest);
2067
2068 assert_matches!(
2069 validate_transaction_builder_manifest(builder),
2070 Err(ManifestValidationError::ProofCannotBePassedToAnotherIntent),
2071 );
2072
2073 let builder = ManifestBuilder::new_subintent_v2();
2074 let lookup = builder.name_lookup();
2075 let subintent_manifest = builder
2076 .create_proof_from_auth_zone_of_all(XRD, "my_proof")
2077 .yield_to_parent((lookup.proof("my_proof"),))
2078 .build_no_validate();
2079 assert_matches!(
2080 validate_subintent_manifest(subintent_manifest),
2081 Err(ManifestValidationError::ProofCannotBePassedToAnotherIntent),
2082 );
2083 }
2084
2085 {
2087 let mut builder = ManifestBuilder::new_v2();
2088 for _ in 0..1001 {
2089 builder = builder.drop_all_proofs();
2090 }
2091 let transaction_manifest = builder.build_no_validate();
2092 assert_matches!(
2093 validate_transaction_manifest(transaction_manifest),
2094 Err(ManifestValidationError::TooManyInstructions),
2095 );
2096 let mut builder = ManifestBuilder::new_v2();
2098 for _ in 0..1000 {
2099 builder = builder.drop_all_proofs();
2100 }
2101 let transaction_manifest = builder.build_no_validate();
2102 assert_matches!(validate_transaction_manifest(transaction_manifest), Ok(_),);
2103
2104 let mut builder = ManifestBuilder::new_subintent_v2();
2105 for _ in 0..1000 {
2106 builder = builder.drop_all_proofs();
2108 }
2109 let subintent_manifest = builder.yield_to_parent(()).build_no_validate();
2110 assert_matches!(
2111 validate_subintent_manifest(subintent_manifest),
2112 Err(ManifestValidationError::TooManyInstructions),
2113 );
2114 let mut builder = ManifestBuilder::new_subintent_v2();
2116 for _ in 0..999 {
2117 builder = builder.drop_all_proofs();
2118 }
2119 let subintent_manifest = builder.yield_to_parent(()).build_no_validate();
2120 assert_matches!(validate_subintent_manifest(subintent_manifest), Ok(_),);
2121 }
2122
2123 {
2125 let invalid_constraints = ManifestResourceConstraints::new().with_unchecked(
2127 XRD,
2128 ManifestResourceConstraint::General(GeneralResourceConstraint {
2129 required_ids: indexset!(NonFungibleLocalId::integer(3)),
2130 lower_bound: LowerBound::NonZero,
2131 upper_bound: UpperBound::Unbounded,
2132 allowed_ids: AllowedIds::Allowlist(indexset!(
2133 NonFungibleLocalId::integer(4),
2134 NonFungibleLocalId::integer(5),
2135 )),
2136 }),
2137 );
2138
2139 let transaction_manifest = ManifestBuilder::new_v2()
2140 .add_raw_instruction_ignoring_all_side_effects(AssertWorktopResourcesOnly {
2141 constraints: invalid_constraints.clone(),
2142 })
2143 .build_no_validate();
2144
2145 assert_matches!(
2146 validate_transaction_manifest(transaction_manifest),
2147 Err(ManifestValidationError::InvalidResourceConstraint),
2148 );
2149
2150 let subintent_manifest = ManifestBuilder::new_subintent_v2()
2151 .assert_worktop_resources_only(invalid_constraints.clone())
2152 .yield_to_parent(())
2153 .build_no_validate();
2154 assert_matches!(
2155 validate_subintent_manifest(subintent_manifest),
2156 Err(ManifestValidationError::InvalidResourceConstraint),
2157 );
2158 }
2159
2160 {
2162 let transaction_manifest = ManifestBuilder::new_v2()
2163 .assert_next_call_returns_include(ManifestResourceConstraints::new())
2164 .drop_all_proofs() .add_test_method_call_with(())
2166 .build_no_validate();
2167 assert_matches!(
2168 validate_transaction_manifest(transaction_manifest),
2169 Err(ManifestValidationError::InstructionFollowingNextCallAssertionWasNotInvocation),
2170 );
2171
2172 let subintent_manifest = ManifestBuilder::new_subintent_v2()
2173 .assert_next_call_returns_only(ManifestResourceConstraints::new())
2174 .drop_auth_zone_signature_proofs()
2175 .yield_to_parent(())
2176 .build_no_validate();
2177 assert_matches!(
2178 validate_subintent_manifest(subintent_manifest),
2179 Err(ManifestValidationError::InstructionFollowingNextCallAssertionWasNotInvocation),
2180 );
2181 }
2182
2183 {
2185 let transaction_manifest = ManifestBuilder::new_v2()
2186 .assert_next_call_returns_include(ManifestResourceConstraints::new())
2187 .build_no_validate();
2188 assert_matches!(
2189 validate_transaction_manifest(transaction_manifest),
2190 Err(ManifestValidationError::ManifestEndedWhilstExpectingNextCallAssertion),
2191 );
2192 }
2193 }
2194
2195 #[test]
2196 fn test_prepare_errors() {
2197 let babylon_validator = TransactionValidator::new_with_static_config(
2198 TransactionValidationConfig::babylon(),
2199 NetworkDefinition::simulator().id,
2200 );
2201 let latest_validator = TransactionValidator::new_for_latest_simulator();
2202
2203 fn create_unvalidated_notarized_transaction_from_manifest(
2204 manifest: TransactionManifestV2,
2205 ) -> NotarizedTransactionV2 {
2206 NotarizedTransactionV2 {
2207 signed_transaction_intent: SignedTransactionIntentV2 {
2208 transaction_intent: TransactionIntentV2 {
2209 transaction_header:
2210 TransactionV2Builder::testing_default_transaction_header(),
2211 root_intent_core: manifest.to_intent_core(
2212 TransactionV2Builder::testing_default_intent_header(),
2213 MessageV2::None,
2214 ),
2215 non_root_subintents: NonRootSubintentsV2(vec![]),
2216 },
2217 transaction_intent_signatures: IntentSignaturesV2::none(),
2218 non_root_subintent_signatures: NonRootSubintentSignaturesV2 {
2219 by_subintent: vec![],
2220 },
2221 },
2222 notary_signature: NotarySignatureV2(SignatureV1::Ed25519(Ed25519Signature(
2223 [0; Ed25519Signature::LENGTH],
2224 ))),
2225 }
2226 }
2227
2228 fn create_unvalidated_raw_notarized_transaction_from_manifest(
2229 manifest: TransactionManifestV2,
2230 ) -> RawNotarizedTransaction {
2231 let transaction = create_unvalidated_notarized_transaction_from_manifest(manifest);
2232 let manually_encoded_transaction = manifest_encode_with_depth_limit(
2233 &AnyTransaction::NotarizedTransactionV2(transaction),
2234 100,
2235 )
2236 .unwrap();
2237 RawNotarizedTransaction::from_vec(manually_encoded_transaction)
2238 }
2239
2240 {
2242 let transaction_v2 = TransactionV2Builder::new_with_test_defaults()
2243 .add_trivial_manifest()
2244 .default_notarize()
2245 .build_minimal_no_validate();
2246
2247 assert_matches!(
2248 transaction_v2.prepare_and_validate(&babylon_validator),
2249 Err(TransactionValidationError::PrepareError(
2250 PrepareError::TransactionTypeNotSupported
2251 )),
2252 );
2253 }
2254
2255 {
2257 let mut manifest_builder = ManifestBuilder::new_v2();
2258 manifest_builder.add_blob(vec![0; 1_100_000]);
2259 let manifest = manifest_builder.build_no_validate();
2260 let transaction = TransactionV2Builder::new_with_test_defaults()
2261 .manifest(manifest)
2262 .default_notarize()
2263 .build_minimal_no_validate();
2264
2265 assert_matches!(
2266 transaction.prepare_and_validate(&latest_validator),
2267 Err(TransactionValidationError::PrepareError(
2268 PrepareError::TransactionTooLarge
2269 )),
2270 );
2271 }
2272
2273 {
2277 let mut nested_value = ManifestValue::unit();
2278 for _ in 0..50 {
2279 nested_value = ManifestValue::tuple([nested_value]);
2280 }
2281 let manifest = ManifestBuilder::new_v2()
2282 .add_raw_instruction_ignoring_all_side_effects(CallMethod {
2283 address: XRD.into(),
2284 method_name: "method".into(),
2285 args: nested_value,
2286 })
2287 .build_no_validate();
2288
2289 let transaction =
2290 create_unvalidated_notarized_transaction_from_manifest(manifest.clone());
2291
2292 assert_matches!(
2294 transaction.prepare_and_validate(&latest_validator),
2295 Err(TransactionValidationError::PrepareError(
2296 PrepareError::EncodeError(EncodeError::MaxDepthExceeded(24))
2297 )),
2298 );
2299
2300 let raw_transaction =
2302 create_unvalidated_raw_notarized_transaction_from_manifest(manifest);
2303 assert_matches!(
2304 raw_transaction.validate(&latest_validator),
2305 Err(TransactionValidationError::PrepareError(
2306 PrepareError::DecodeError(DecodeError::MaxDepthExceeded(24))
2307 )),
2308 );
2309 }
2310
2311 {
2313 {
2315 let mut manifest_builder = ManifestBuilder::new_v2();
2316 for i in 0..65 {
2317 manifest_builder.add_blob(vec![0; i as usize]);
2318 }
2319 let transaction = create_unvalidated_notarized_transaction_from_manifest(
2320 manifest_builder.build_no_validate(),
2321 );
2322
2323 assert_matches!(
2324 transaction.prepare_and_validate(&latest_validator),
2325 Err(TransactionValidationError::PrepareError(
2326 PrepareError::TooManyValues {
2327 value_type: ValueType::Blob,
2328 actual: 65,
2329 max: 64,
2330 }
2331 )),
2332 );
2333 }
2334
2335 {
2337 let mut transaction = TransactionV2Builder::new_with_test_defaults()
2338 .add_trivial_manifest()
2339 .default_notarize()
2340 .build_minimal_no_validate();
2341 let subintents = (0..33)
2342 .map(|i| {
2343 create_leaf_partial_transaction(i, 0)
2344 .partial_transaction
2345 .partial_transaction
2346 .root_subintent
2347 })
2348 .collect::<Vec<_>>();
2349 transaction
2350 .signed_transaction_intent
2351 .transaction_intent
2352 .non_root_subintents = NonRootSubintentsV2(subintents);
2353 assert_matches!(
2354 transaction.prepare_and_validate(&latest_validator),
2355 Err(TransactionValidationError::PrepareError(
2356 PrepareError::TooManyValues {
2357 value_type: ValueType::Subintent,
2358 actual: 33,
2359 max: 32,
2360 }
2361 )),
2362 );
2363 }
2364
2365 {
2367 let mut transaction = TransactionV2Builder::new_with_test_defaults()
2368 .add_trivial_manifest()
2369 .default_notarize()
2370 .build_minimal_no_validate();
2371 let child_specifiers = (0..33)
2372 .map(|i| ChildSubintentSpecifier {
2373 hash: SubintentHash::from_bytes([i as u8; Hash::LENGTH]),
2374 })
2375 .collect();
2376 transaction
2377 .signed_transaction_intent
2378 .transaction_intent
2379 .root_intent_core
2380 .children = ChildSubintentSpecifiersV2 {
2381 children: child_specifiers,
2382 };
2383 assert_matches!(
2384 transaction.prepare_and_validate(&latest_validator),
2385 Err(TransactionValidationError::PrepareError(
2386 PrepareError::TooManyValues {
2387 value_type: ValueType::ChildSubintentSpecifier,
2388 actual: 33,
2389 max: 32,
2390 }
2391 )),
2392 );
2393 }
2394
2395 {
2397 let mut transaction = TransactionV2Builder::new_with_test_defaults()
2398 .add_trivial_manifest()
2399 .default_notarize()
2400 .build_minimal_no_validate();
2401 let subintent_signature_batches = (0..33)
2402 .map(|_| IntentSignaturesV2::none())
2403 .collect::<Vec<_>>();
2404 transaction
2405 .signed_transaction_intent
2406 .non_root_subintent_signatures = NonRootSubintentSignaturesV2 {
2407 by_subintent: subintent_signature_batches,
2408 };
2409 assert_matches!(
2410 transaction.prepare_and_validate(&latest_validator),
2411 Err(TransactionValidationError::PrepareError(
2412 PrepareError::TooManyValues {
2413 value_type: ValueType::SubintentSignatureBatches,
2414 actual: 33,
2415 max: 32,
2416 }
2417 )),
2418 );
2419 }
2420 }
2421
2422 {
2427 let raw_transaction = TransactionV2Builder::new_with_test_defaults()
2428 .add_trivial_manifest()
2429 .default_notarize()
2430 .build_minimal_no_validate()
2431 .to_raw()
2432 .unwrap();
2433
2434 let mut amended_payload = raw_transaction.to_vec();
2435 amended_payload[2] = 4;
2436 let amended_raw = RawNotarizedTransaction::from_vec(amended_payload);
2437 assert_eq!(
2438 amended_raw.validate(&latest_validator),
2439 Err(TransactionValidationError::PrepareError(
2440 PrepareError::UnexpectedTransactionDiscriminator { actual: Some(4) }
2441 ))
2442 )
2443 }
2444 }
2445}