radix_transactions/validation/
transaction_validator_v2.rs

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: _, // Checked to be 0 in the manifest validator.
13            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: _, // Checked to be 0 in the manifest validator.
41            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    // This method is public so it can be used by the toolkit.
58    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        // We delay signature validation until after the other validations as it's more expensive.
119        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    // This method is public so it can be used by the toolkit.
158    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    // This method is public so it can be used by the toolkit.
178    pub fn validate_intent_header_v2(
179        &self,
180        header: &IntentHeaderV2,
181        aggregation: &mut AcrossIntentAggregation,
182    ) -> Result<(), HeaderValidationError> {
183        // Network
184        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        // Epoch
191        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    // This method is public so it can be used by the toolkit.
222    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                    // Can't overflow because decryptor count << size of a transaction < 1MB < usize,
269                    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    // This method is public so it can be used by the toolkit.
283    /// The `is_subintent` property indicates whether it should be treated as a subintent.
284    /// A subintent is able to `YIELD_TO_PARENT` and is required to end with a `YIELD_TO_PARENT`.
285    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        // SubintentStructureError::DuplicateSubintent
531        {
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        // SubintentStructureError::SubintentHasMultipleParents
562        // ====================================================
563        // CASE 1 - Two duplicates as children in the same intent
564        // =======> This isn't possible because `ChildSubintentSpecifiersV2` wraps an `IndexSet<ChildSubintentSpecifier>`
565        // Case 2 - Both duplicates across different intents
566        // =======> This is tested below
567        {
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        // SubintentStructureError::ChildSubintentNotIncludedInTransaction(SubintentHash)
602        {
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        // SubintentStructureError::SubintentExceedsMaxDepth
633        {
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        // SubintentStructureError::SubintentIsNotReachableFromTheTransactionIntent
666        // ========================================================================
667        // CASE 1 - The subintent is superfluous / has no parent
668        // This is tested below
669        //
670        // CASE 2 - Without a "no parent" short-circuit.
671        // To hit this error (but none of the previous errors) requires that we have
672        // a cycle in the subintent graph.
673        //
674        // But, because parents include a subintent hash of their direct children,
675        // which is itself part of their hash, a cycle would require a hash collision!
676        //
677        // But we can hack around this by explicitly overwriting the prepared subintent
678        // hashes.
679        {
680            // CASE 1 - The subintent has no parent
681            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            // CASE 2 - Without a potential "no parent" short-circuit
715            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            // We overwrite the subintent hash to the faked hash
758            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        // SubintentStructureError::MismatchingYieldChildAndYieldParentCountsForSubintent
778        {
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            // CASE 1: We yield twice to it, but it yields to us only once
783            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            // CASE 2: We yield zero times to it
804            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            // CASE 3: More complex example, between two subintents, with 2 parent and 3 child yields:
821            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    // NOTE: This is very similar to the V1 tests, just adjusted to the V2 models
856    #[test]
857    fn test_valid_messages() {
858        // None
859        {
860            let message = MessageV2::None;
861            assert_matches!(validate_transaction_with_message(message), Ok(_),);
862        }
863        // Plaintext
864        {
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        // Encrypted
872        {
873            // Note - this isn't actually a validly encrypted message,
874            // this just shows that a sufficiently valid encrypted message can pass validation
875            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    // NOTE: This is very similar to the V1 tests, just adjusted to the V2 models
898    #[test]
899    fn test_invalid_message_errors() {
900        // MimeTypeTooLong
901        {
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        // PlaintextMessageTooLong
913        {
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        // EncryptedMessageTooLong
929        {
930            let mut message_which_is_too_long: String = "".to_owned();
931            while message_which_is_too_long.len() <= 2048 + 50 {
932                // Some more bytes for the AES padding
933                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        // NoDecryptors
953        {
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        // NoDecryptorsForCurveType
965        {
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        // MismatchingDecryptorCurves
984        {
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        // TooManyDecryptors
1006        {
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, // 1000 from subintent, 1 from transaction intent
1111                    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        // InvalidEpochRange
1125        {
1126            // CASE 1 - Negative range
1127            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            // CASE 2 - Equal range
1162            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            // CASE 3 - Range too large
1197            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        // InvalidTimestampRange
1233        {
1234            // CASE 1 - Negative range
1235            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            // CASE 2 - Equal range
1271            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            // And for good measure, let's test some valid ranges:
1306            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        // InvalidNetwork
1349        {
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            // And for good measure, demonstrate that the network agnostic validator is okay with this:
1387            // (even with different intents being for different networks(!) - which is a bit weird, but
1388            // it's only intended for testing)
1389            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        // InvalidTip
1403        {
1404            // Note - min tip is 0, so we can't hit that error
1405            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        // NoValidEpochRangeAcrossAllIntents
1419        {
1420            // Subintent doesn't overlap with TransactionIntent
1421            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            // Only one pair of subintents don't overlap
1443            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            // There is an overlap
1475            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        // NoValidTimestampRangeAcrossAllIntents
1506        {
1507            // Subintent doesn't overlap with TransactionIntent
1508            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            // Only one pair of subintents don't overlap
1528            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            // There is an overlap
1555            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        // DuplicateBlob(ManifestBlobRef)
1648        {
1649            // This is not actually possible to get in TransactionV2, because the manifest stores an IndexMap<Hash, Bytes>.
1650            // Currently we remove duplicates at the `PreparedBlobsV1` layer.
1651        }
1652
1653        // BlobNotRegistered(ManifestBlobRef)
1654        {
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        // BucketNotYetCreated(ManifestBucket)
1679        {
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        // BucketAlreadyUsed(ManifestBucket, String)
1704        {
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        // BucketConsumedWhilstLockedByProof(ManifestBucket, String)
1733        {
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        // ProofNotYetCreated(ManifestProof)
1762        {
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        // ProofAlreadyUsed(ManifestProof, String)
1787        {
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        // AddressReservationNotYetCreated(ManifestAddressReservation)
1816        {
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        // AddressReservationAlreadyUsed(ManifestAddressReservation, String)
1841        {
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        // NamedAddressNotYetCreated(ManifestNamedAddress)
1880        {
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        // ChildIntentNotRegistered(ManifestNamedIntent)
1905        {
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        // DanglingBucket(ManifestBucket, String)
1936        {
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        // DanglingAddressReservation(ManifestAddressReservation, String)
1961        {
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        // ArgsEncodeError(EncodeError)
1996        {
1997            // Hard to create when coming from a prepared transaction, because the values
1998            // come from being decoded
1999        }
2000
2001        // ArgsDecodeError(DecodeError)
2002        {
2003            // Hard to create when coming from a prepared transaction, because the values
2004            // come from being decoded
2005        }
2006
2007        // InstructionNotSupportedInTransactionIntent
2008        {
2009            // YIELD_TO_PARENT
2010            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            // VERIFY_PARENT
2022            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        // SubintentDoesNotEndWithYieldToParent
2034        {
2035            // CASE 1: At least 1 instruction
2036            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            // CASE 2: No instructions
2046            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        // ProofCannotBePassedToAnotherIntent
2055        {
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        // TooManyInstructions
2086        {
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            // And test that one less is fine
2097            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                // Only 1000 because we're adding a yield_to_parent below
2107                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            // And test that one less is fine
2115            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        // InvalidResourceConstraint
2124        {
2125            // Invalid because there's no overlap between `required_ids` and `allowed_ids`
2126            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        // InstructionFollowingNextCallAssertionWasNotInvocation
2161        {
2162            let transaction_manifest = ManifestBuilder::new_v2()
2163                .assert_next_call_returns_include(ManifestResourceConstraints::new())
2164                .drop_all_proofs() // This is not an invocation
2165                .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        // ManifestEndedWhilstExpectingNextCallAssertion
2184        {
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        // TransactionTypeNotSupported
2241        {
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        // TransactionTooLarge
2256        {
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        // EncodeError(EncodeError) and DecodeError(DecodeError)
2274        // Note that EncodeError doesn't happen as part of preparation in the node, only when preparing from
2275        // an encodable model. But we can test it here anyway...
2276        {
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            // We get an EncodeError when preparing directly from the model
2293            assert_matches!(
2294                transaction.prepare_and_validate(&latest_validator),
2295                Err(TransactionValidationError::PrepareError(
2296                    PrepareError::EncodeError(EncodeError::MaxDepthExceeded(24))
2297                )),
2298            );
2299
2300            // We get a DecodeError when preparing directly from the raw transaction
2301            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        // TooManyValues { value_type: ValueType, actual: usize, max: usize, }
2312        {
2313            // Blob
2314            {
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            // Subintent
2336            {
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            // ChildSubintentSpecifier
2366            {
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            // SubintentSignatureBatches
2396            {
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        // LengthOverflow
2423        // -> Rather hard to test, we can leave this.
2424
2425        // UnexpectedTransactionDiscriminator
2426        {
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}