radix_transactions/validation/
transaction_validator_v1.rs

1use crate::internal_prelude::*;
2
3impl TransactionValidator {
4    #[allow(deprecated)]
5    pub fn validate_notarized_v1(
6        &self,
7        transaction: PreparedNotarizedTransactionV1,
8    ) -> Result<ValidatedNotarizedTransactionV1, TransactionValidationError> {
9        let transaction_intent = &transaction.signed_intent.intent;
10
11        let signatures = AllPendingSignatureValidations::new_with_root(
12            TransactionVersion::V1,
13            &self.config,
14            transaction_intent.transaction_intent_hash().into(),
15            PendingIntentSignatureValidations::TransactionIntent {
16                notary_is_signatory: transaction_intent.header.inner.notary_is_signatory,
17                notary_public_key: transaction_intent.header.inner.notary_public_key,
18                notary_signature: transaction.notary_signature.inner.0,
19                notarized_hash: transaction.signed_transaction_intent_hash(),
20                intent_signatures: transaction
21                    .signed_intent
22                    .intent_signatures
23                    .inner
24                    .signatures
25                    .as_slice(),
26                signed_hash: transaction_intent.transaction_intent_hash(),
27            },
28        )?;
29
30        let aggregation = self
31            .validate_intent_v1(&transaction.signed_intent.intent)
32            .map_err(|err| {
33                TransactionValidationError::IntentValidationError(
34                    TransactionValidationErrorLocation::RootTransactionIntent(
35                        transaction_intent.transaction_intent_hash(),
36                    ),
37                    err,
38                )
39            })?;
40        let _ = aggregation.finalize(&self.config)?; // Don't use the overall validity range in V1
41
42        let encoded_instructions =
43            manifest_encode(&transaction.signed_intent.intent.instructions.inner.0)?;
44
45        let SignatureValidationSummary {
46            root_signer_keys: signer_keys,
47            non_root_signer_keys: _, // Not used in V1
48            total_signature_validations: num_of_signature_validations,
49        } = signatures.validate_all()?;
50
51        Ok(ValidatedNotarizedTransactionV1 {
52            prepared: transaction,
53            encoded_instructions,
54            signer_keys,
55            num_of_signature_validations,
56        })
57    }
58
59    #[allow(deprecated)]
60    pub fn validate_preview_intent_v1(
61        &self,
62        preview_intent: PreviewIntentV1,
63    ) -> Result<ValidatedPreviewIntent, TransactionValidationError> {
64        let fake_intent_hash = SimulatedTransactionIntentNullification.transaction_intent_hash();
65        let intent = preview_intent.intent.prepare(self.preparation_settings())?;
66
67        let aggregation = self.validate_intent_v1(&intent).map_err(|err| {
68            TransactionValidationError::IntentValidationError(
69                TransactionValidationErrorLocation::RootTransactionIntent(fake_intent_hash),
70                err,
71            )
72        })?;
73        aggregation.finalize(&self.config)?;
74
75        let encoded_instructions = manifest_encode(&intent.instructions.inner.0)?;
76
77        Ok(ValidatedPreviewIntent {
78            intent,
79            encoded_instructions,
80            signer_public_keys: preview_intent.signer_public_keys,
81            flags: preview_intent.flags,
82        })
83    }
84
85    // This method is public so it can be used by the toolkit.
86    #[allow(deprecated)]
87    pub fn validate_intent_v1(
88        &self,
89        intent: &PreparedIntentV1,
90    ) -> Result<AcrossIntentAggregation, IntentValidationError> {
91        let mut aggregation = AcrossIntentAggregation::start();
92        self.validate_header_v1(&intent.header.inner)?;
93        self.validate_message_v1(&intent.message.inner)?;
94        aggregation.record_reference_count(intent.instructions.references.len(), &self.config)?;
95        self.validate_instructions_v1(&intent.instructions.inner.0, &intent.blobs.blobs_by_hash)?;
96
97        Ok(aggregation)
98    }
99
100    pub fn validate_instructions_v1(
101        &self,
102        instructions: &[InstructionV1],
103        blobs: &IndexMap<Hash, Vec<u8>>,
104    ) -> Result<(), IntentValidationError> {
105        if instructions.len() > self.config.max_instructions {
106            return Err(ManifestValidationError::TooManyInstructions.into());
107        }
108
109        match self.config.manifest_validation {
110            ManifestValidationRuleset::BabylonBasicValidator => self
111                .validate_instructions_basic_v1(instructions)
112                .map_err(|err| err.into()),
113            ManifestValidationRuleset::Interpreter(specifier) => StaticManifestInterpreter::new(
114                ValidationRuleset::for_specifier(specifier),
115                &EphemeralManifest::new_childless_transaction_manifest(instructions, blobs),
116            )
117            .validate()
118            .map_err(|err| err.into()),
119        }
120    }
121
122    pub fn validate_instructions_basic_v1(
123        &self,
124        instructions: &[InstructionV1],
125    ) -> Result<(), ManifestBasicValidatorError> {
126        let mut id_validator = BasicManifestValidator::new();
127        for instruction in instructions {
128            match instruction.effect() {
129                ManifestInstructionEffect::CreateBucket { .. } => {
130                    let _ = id_validator.new_bucket();
131                }
132                ManifestInstructionEffect::CreateProof { source_amount, .. } => {
133                    let _ = id_validator.new_proof(source_amount.proof_kind())?;
134                }
135                ManifestInstructionEffect::ConsumeBucket {
136                    consumed_bucket: bucket,
137                    ..
138                } => {
139                    id_validator.drop_bucket(&bucket)?;
140                }
141                ManifestInstructionEffect::ConsumeProof {
142                    consumed_proof: proof,
143                    ..
144                } => {
145                    id_validator.drop_proof(&proof)?;
146                }
147                ManifestInstructionEffect::CloneProof { cloned_proof, .. } => {
148                    let _ = id_validator.clone_proof(&cloned_proof)?;
149                }
150                ManifestInstructionEffect::DropManyProofs {
151                    drop_all_named_proofs,
152                    ..
153                } => {
154                    if drop_all_named_proofs {
155                        id_validator.drop_all_named_proofs()?;
156                    }
157                }
158                ManifestInstructionEffect::Invocation { args, .. } => {
159                    id_validator.process_call_data(args)?;
160                }
161                ManifestInstructionEffect::CreateAddressAndReservation { .. } => {
162                    let _ = id_validator.new_address_reservation();
163                    id_validator.new_named_address();
164                }
165                ManifestInstructionEffect::ResourceAssertion { .. } => {}
166                ManifestInstructionEffect::Verification { .. } => {
167                    unreachable!("No InstructionV1 returns this effect");
168                }
169            }
170        }
171        Ok(())
172    }
173
174    pub fn validate_header_v1(
175        &self,
176        header: &TransactionHeaderV1,
177    ) -> Result<(), HeaderValidationError> {
178        // network
179        if let Some(required_network_id) = self.required_network_id {
180            if header.network_id != required_network_id {
181                return Err(HeaderValidationError::InvalidNetwork);
182            }
183        }
184
185        // epoch
186        if header.end_epoch_exclusive <= header.start_epoch_inclusive {
187            return Err(HeaderValidationError::InvalidEpochRange);
188        }
189        let max_end_epoch = header
190            .start_epoch_inclusive
191            .after(self.config.max_epoch_range)
192            .ok_or(HeaderValidationError::InvalidEpochRange)?;
193        if header.end_epoch_exclusive > max_end_epoch {
194            return Err(HeaderValidationError::InvalidEpochRange);
195        }
196
197        // tip percentage
198        if header.tip_percentage < self.config.min_tip_percentage
199            || header.tip_percentage > self.config.max_tip_percentage
200        {
201            return Err(HeaderValidationError::InvalidTip);
202        }
203
204        Ok(())
205    }
206
207    pub fn validate_message_v1(&self, message: &MessageV1) -> Result<(), InvalidMessageError> {
208        let validation = &self.config.message_validation;
209        match message {
210            MessageV1::None => {}
211            MessageV1::Plaintext(plaintext_message) => {
212                let PlaintextMessageV1 { mime_type, message } = plaintext_message;
213                if mime_type.len() > validation.max_mime_type_length {
214                    return Err(InvalidMessageError::MimeTypeTooLong {
215                        actual: mime_type.len(),
216                        permitted: validation.max_mime_type_length,
217                    });
218                }
219                if message.len() > validation.max_plaintext_message_length {
220                    return Err(InvalidMessageError::PlaintextMessageTooLong {
221                        actual: message.len(),
222                        permitted: validation.max_plaintext_message_length,
223                    });
224                }
225            }
226            MessageV1::Encrypted(encrypted_message) => {
227                let EncryptedMessageV1 {
228                    encrypted,
229                    decryptors_by_curve,
230                } = encrypted_message;
231                if encrypted.0.len() > validation.max_encrypted_message_length {
232                    return Err(InvalidMessageError::EncryptedMessageTooLong {
233                        actual: encrypted.0.len(),
234                        permitted: validation.max_encrypted_message_length,
235                    });
236                }
237                if decryptors_by_curve.is_empty() {
238                    return Err(InvalidMessageError::NoDecryptors);
239                }
240                let mut total_decryptors = 0;
241                for (curve_type, decryptors) in decryptors_by_curve.iter() {
242                    if decryptors.curve_type() != *curve_type {
243                        return Err(InvalidMessageError::MismatchingDecryptorCurves {
244                            actual: decryptors.curve_type(),
245                            expected: *curve_type,
246                        });
247                    }
248                    if decryptors.number_of_decryptors() == 0 {
249                        return Err(InvalidMessageError::NoDecryptorsForCurveType {
250                            curve_type: decryptors.curve_type(),
251                        });
252                    }
253                    // Can't overflow because decryptor count << size of a transaction < 1MB < usize,
254                    total_decryptors += decryptors.number_of_decryptors();
255                }
256                if total_decryptors > validation.max_decryptors {
257                    return Err(InvalidMessageError::TooManyDecryptors {
258                        actual: total_decryptors,
259                        permitted: validation.max_decryptors,
260                    });
261                }
262            }
263        }
264        Ok(())
265    }
266}
267
268#[cfg(test)]
269mod tests {
270    use crate::internal_prelude::*;
271
272    macro_rules! assert_invalid_tx {
273        ($result: pat, ($start_epoch: expr, $end_epoch: expr, $nonce: expr, $signers: expr, $notary: expr)) => {{
274            let validator = TransactionValidator::new_for_latest_simulator();
275            assert_matches!(
276                create_transaction($start_epoch, $end_epoch, $nonce, $signers, $notary)
277                    .prepare_and_validate(&validator)
278                    .expect_err("Should be an error"),
279                $result,
280            );
281        }};
282    }
283
284    #[test]
285    fn test_invalid_header() {
286        assert_invalid_tx!(
287            TransactionValidationError::IntentValidationError(
288                TransactionValidationErrorLocation::RootTransactionIntent(_),
289                IntentValidationError::HeaderValidationError(
290                    HeaderValidationError::InvalidEpochRange
291                ),
292            ),
293            (Epoch::zero(), Epoch::zero(), 5, vec![1], 2)
294        );
295        assert_invalid_tx!(
296            TransactionValidationError::IntentValidationError(
297                TransactionValidationErrorLocation::RootTransactionIntent(_),
298                IntentValidationError::HeaderValidationError(
299                    HeaderValidationError::InvalidEpochRange
300                ),
301            ),
302            (
303                Epoch::zero(),
304                Epoch::of(TransactionValidationConfig::latest().max_epoch_range + 1),
305                5,
306                vec![1],
307                2
308            )
309        );
310    }
311
312    #[test]
313    fn test_epoch_overflow() {
314        assert_invalid_tx!(
315            TransactionValidationError::IntentValidationError(
316                TransactionValidationErrorLocation::RootTransactionIntent(_),
317                IntentValidationError::HeaderValidationError(
318                    HeaderValidationError::InvalidEpochRange
319                ),
320            ),
321            (Epoch::of(u64::MAX - 5), Epoch::of(u64::MAX), 5, vec![1], 2)
322        );
323    }
324
325    #[test]
326    fn test_too_many_signatures() {
327        assert_invalid_tx!(
328            TransactionValidationError::SignatureValidationError(
329                TransactionValidationErrorLocation::RootTransactionIntent(_),
330                SignatureValidationError::TooManySignatures {
331                    total: 19,
332                    limit: 16,
333                }
334            ),
335            (Epoch::zero(), Epoch::of(100), 5, (1..20).collect(), 2)
336        );
337    }
338
339    #[test]
340    fn test_duplicate_signers() {
341        assert_invalid_tx!(
342            TransactionValidationError::SignatureValidationError(
343                TransactionValidationErrorLocation::RootTransactionIntent(_),
344                SignatureValidationError::DuplicateSigner
345            ),
346            (Epoch::zero(), Epoch::of(100), 5, vec![1, 1], 2)
347        );
348    }
349
350    #[test]
351    fn test_valid_preview() {
352        // Build the whole transaction but only really care about the intent
353        let tx = create_transaction(Epoch::zero(), Epoch::of(100), 5, vec![1, 2], 2);
354
355        let validator = TransactionValidator::new_for_latest_simulator();
356
357        let preview_intent = PreviewIntentV1 {
358            intent: tx.signed_intent.intent,
359            signer_public_keys: Vec::new(),
360            flags: PreviewFlags {
361                use_free_credit: true,
362                assume_all_signature_proofs: false,
363                skip_epoch_check: false,
364                disable_auth: false,
365            },
366        };
367
368        let result = validator.validate_preview_intent_v1(preview_intent);
369
370        assert!(result.is_ok());
371    }
372
373    #[test]
374    fn test_valid_messages() {
375        // None
376        {
377            let message = MessageV1::None;
378            let result = validate_default(&create_transaction_with_message(message));
379            assert!(result.is_ok());
380        }
381        // Plaintext
382        {
383            let message = MessageV1::Plaintext(PlaintextMessageV1 {
384                mime_type: "text/plain".to_owned(),
385                message: MessageContentsV1::String("Hello world!".to_string()),
386            });
387            let result = validate_default(&create_transaction_with_message(message));
388            assert!(result.is_ok());
389        }
390        // Encrypted
391        {
392            // Note - this isn't actually a validly encrypted message,
393            // this just shows that a sufficiently valid encrypted message can pass validation
394            let message = MessageV1::Encrypted(EncryptedMessageV1 {
395                encrypted: AesGcmPayload(vec![]),
396                decryptors_by_curve: indexmap!(
397                    CurveType::Ed25519 => DecryptorsByCurve::Ed25519 {
398                        dh_ephemeral_public_key: Ed25519PublicKey([0; Ed25519PublicKey::LENGTH]),
399                        decryptors: indexmap!(
400                            PublicKeyFingerprint([0; PublicKeyFingerprint::LENGTH]) => AesWrapped128BitKey([0; AesWrapped128BitKey::LENGTH]),
401                        ),
402                    },
403                    CurveType::Secp256k1 => DecryptorsByCurve::Secp256k1 {
404                        dh_ephemeral_public_key: Secp256k1PublicKey([0; Secp256k1PublicKey::LENGTH]),
405                        decryptors: indexmap!(
406                            PublicKeyFingerprint([0; PublicKeyFingerprint::LENGTH]) => AesWrapped128BitKey([0; AesWrapped128BitKey::LENGTH]),
407                            PublicKeyFingerprint([1; PublicKeyFingerprint::LENGTH]) => AesWrapped128BitKey([0; AesWrapped128BitKey::LENGTH]),
408                        ),
409                    },
410                ),
411            });
412            let result = validate_default(&create_transaction_with_message(message));
413            assert!(result.is_ok());
414        }
415    }
416
417    #[test]
418    fn test_invalid_message_errors() {
419        // MimeTypeTooLong
420        {
421            let message = MessageV1::Plaintext(PlaintextMessageV1 {
422                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(),
423                message: MessageContentsV1::String("Hello".to_string()),
424            });
425            let error =
426                validate_default_expecting_message_error(&create_transaction_with_message(message));
427            assert_matches!(error, InvalidMessageError::MimeTypeTooLong { .. })
428        }
429
430        // PlaintextMessageTooLong
431        {
432            let mut long_message: String = "".to_owned();
433            while long_message.len() <= 2048 {
434                long_message.push_str("more text please!");
435            }
436            let message = MessageV1::Plaintext(PlaintextMessageV1 {
437                mime_type: "text/plain".to_owned(),
438                message: MessageContentsV1::String(long_message),
439            });
440            let error =
441                validate_default_expecting_message_error(&create_transaction_with_message(message));
442            assert_matches!(error, InvalidMessageError::PlaintextMessageTooLong { .. })
443        }
444
445        // EncryptedMessageTooLong
446        {
447            let mut message_which_is_too_long: String = "".to_owned();
448            while message_which_is_too_long.len() <= 2048 + 50 {
449                // Some more bytes for the AES padding
450                message_which_is_too_long.push_str("more text please!");
451            }
452            let message = MessageV1::Encrypted(EncryptedMessageV1 {
453                encrypted: AesGcmPayload(message_which_is_too_long.as_bytes().to_vec()),
454                decryptors_by_curve: indexmap!(
455                    CurveType::Ed25519 => DecryptorsByCurve::Ed25519 {
456                        dh_ephemeral_public_key: Ed25519PublicKey([0; Ed25519PublicKey::LENGTH]),
457                        decryptors: indexmap!(
458                            PublicKeyFingerprint([0; PublicKeyFingerprint::LENGTH]) => AesWrapped128BitKey([0; AesWrapped128BitKey::LENGTH]),
459                        ),
460                    }
461                ),
462            });
463            let error =
464                validate_default_expecting_message_error(&create_transaction_with_message(message));
465            assert_matches!(error, InvalidMessageError::EncryptedMessageTooLong { .. })
466        }
467
468        // NoDecryptors
469        {
470            let message = MessageV1::Encrypted(EncryptedMessageV1 {
471                encrypted: AesGcmPayload(vec![]),
472                decryptors_by_curve: indexmap!(),
473            });
474            let error =
475                validate_default_expecting_message_error(&create_transaction_with_message(message));
476            assert_matches!(error, InvalidMessageError::NoDecryptors)
477        }
478
479        // NoDecryptorsForCurveType
480        {
481            let message = MessageV1::Encrypted(EncryptedMessageV1 {
482                encrypted: AesGcmPayload(vec![]),
483                decryptors_by_curve: indexmap!(
484                    CurveType::Ed25519 => DecryptorsByCurve::Ed25519 {
485                        dh_ephemeral_public_key: Ed25519PublicKey([0; Ed25519PublicKey::LENGTH]),
486                        decryptors: indexmap!(),
487                    }
488                ),
489            });
490            let error =
491                validate_default_expecting_message_error(&create_transaction_with_message(message));
492            assert_matches!(
493                error,
494                InvalidMessageError::NoDecryptorsForCurveType {
495                    curve_type: CurveType::Ed25519
496                }
497            )
498        }
499
500        // MismatchingDecryptorCurves
501        {
502            let message = MessageV1::Encrypted(EncryptedMessageV1 {
503                encrypted: AesGcmPayload(vec![]),
504                decryptors_by_curve: indexmap!(
505                    CurveType::Ed25519 => DecryptorsByCurve::Secp256k1 {
506                        dh_ephemeral_public_key: Secp256k1PublicKey([0; Secp256k1PublicKey::LENGTH]),
507                        decryptors: indexmap!(
508                            PublicKeyFingerprint([0; PublicKeyFingerprint::LENGTH]) => AesWrapped128BitKey([0; AesWrapped128BitKey::LENGTH]),
509                        ),
510                    }
511                ),
512            });
513            let error =
514                validate_default_expecting_message_error(&create_transaction_with_message(message));
515            assert_matches!(
516                error,
517                InvalidMessageError::MismatchingDecryptorCurves {
518                    actual: CurveType::Secp256k1,
519                    expected: CurveType::Ed25519
520                }
521            )
522        }
523
524        // TooManyDecryptors
525        {
526            let mut decryptors = IndexMap::<PublicKeyFingerprint, AesWrapped128BitKey>::default();
527            for i in 0..30 {
528                decryptors.insert(
529                    PublicKeyFingerprint([0, 0, 0, 0, 0, 0, 0, i as u8]),
530                    AesWrapped128BitKey([0; AesWrapped128BitKey::LENGTH]),
531                );
532            }
533            let message = MessageV1::Encrypted(EncryptedMessageV1 {
534                encrypted: AesGcmPayload(vec![]),
535                decryptors_by_curve: indexmap!(
536                    CurveType::Ed25519 => DecryptorsByCurve::Ed25519 {
537                        dh_ephemeral_public_key: Ed25519PublicKey([0; Ed25519PublicKey::LENGTH]),
538                        decryptors,
539                    }
540                ),
541            });
542            let error =
543                validate_default_expecting_message_error(&create_transaction_with_message(message));
544            assert_matches!(
545                error,
546                InvalidMessageError::TooManyDecryptors {
547                    actual: 30,
548                    permitted: 20
549                }
550            )
551        }
552    }
553
554    fn validate_default_expecting_message_error(
555        transaction: &NotarizedTransactionV1,
556    ) -> InvalidMessageError {
557        match validate_default(transaction).expect_err("Expected validation error") {
558            TransactionValidationError::IntentValidationError(
559                _,
560                IntentValidationError::InvalidMessage(error),
561            ) => error,
562            error => {
563                panic!("Expected InvalidMessage error, got: {:?}", error)
564            }
565        }
566    }
567
568    fn validate_default(
569        transaction: &NotarizedTransactionV1,
570    ) -> Result<(), TransactionValidationError> {
571        let validator = TransactionValidator::new_for_latest_simulator();
572        transaction.prepare_and_validate(&validator).map(|_| ())
573    }
574
575    fn create_transaction_with_message(message: MessageV1) -> NotarizedTransactionV1 {
576        let sk_notary = Secp256k1PrivateKey::from_u64(1).unwrap();
577
578        let mut builder = TransactionBuilder::new()
579            .header(TransactionHeaderV1 {
580                network_id: NetworkDefinition::simulator().id,
581                start_epoch_inclusive: Epoch::of(1),
582                end_epoch_exclusive: Epoch::of(10),
583                nonce: 0,
584                notary_public_key: sk_notary.public_key().into(),
585                notary_is_signatory: false,
586                tip_percentage: 5,
587            })
588            .manifest(ManifestBuilder::new().drop_auth_zone_proofs().build())
589            .message(message);
590
591        builder = builder.notarize(&sk_notary);
592
593        builder.build()
594    }
595
596    fn create_transaction(
597        start_epoch: Epoch,
598        end_epoch: Epoch,
599        nonce: u32,
600        signers: Vec<u64>,
601        notary: u64,
602    ) -> NotarizedTransactionV1 {
603        create_transaction_advanced(
604            start_epoch,
605            end_epoch,
606            nonce,
607            signers,
608            notary,
609            ManifestBuilder::new().drop_auth_zone_proofs().build(),
610        )
611    }
612
613    fn create_transaction_advanced(
614        start_epoch: Epoch,
615        end_epoch: Epoch,
616        nonce: u32,
617        signers: Vec<u64>,
618        notary: u64,
619        manifest: TransactionManifestV1,
620    ) -> NotarizedTransactionV1 {
621        let sk_notary = Secp256k1PrivateKey::from_u64(notary).unwrap();
622
623        let mut builder = TransactionBuilder::new()
624            .header(TransactionHeaderV1 {
625                network_id: NetworkDefinition::simulator().id,
626                start_epoch_inclusive: start_epoch,
627                end_epoch_exclusive: end_epoch,
628                nonce,
629                notary_public_key: sk_notary.public_key().into(),
630                notary_is_signatory: false,
631                tip_percentage: 5,
632            })
633            .manifest(manifest);
634
635        for signer in signers {
636            builder = builder.sign(Secp256k1PrivateKey::from_u64(signer).unwrap());
637        }
638        builder = builder.notarize(&sk_notary);
639
640        builder.build()
641    }
642
643    #[test]
644    fn test_drop_bucket_before_proof() {
645        let transaction = create_transaction_advanced(
646            Epoch::of(0),
647            Epoch::of(40),
648            123,
649            vec![55],
650            66,
651            ManifestBuilder::new()
652                .take_from_worktop(XRD, dec!(100), "bucket")
653                .create_proof_from_bucket_of_amount("bucket", dec!(5), "proof1")
654                .return_to_worktop("bucket")
655                .drop_proof("proof1")
656                .build_no_validate(),
657        );
658        let validator = TransactionValidator::new_for_latest_simulator();
659        assert_matches!(
660            transaction.prepare_and_validate(&validator),
661            Err(TransactionValidationError::IntentValidationError(
662                _,
663                IntentValidationError::ManifestValidationError(
664                    ManifestValidationError::BucketConsumedWhilstLockedByProof(
665                        ManifestBucket(0),
666                        _,
667                    )
668                )
669            ))
670        );
671    }
672
673    #[test]
674    fn test_clone_invalid_proof() {
675        let transaction = create_transaction_advanced(
676            Epoch::of(0),
677            Epoch::of(40),
678            123,
679            vec![55],
680            66,
681            ManifestBuilder::new()
682                .take_from_worktop(XRD, dec!(100), "bucket")
683                .create_proof_from_bucket_of_amount("bucket", dec!(5), "proof1")
684                .then(|builder| {
685                    let lookup = builder.name_lookup();
686                    let proof_id = lookup.proof("proof1");
687
688                    builder
689                        .drop_proof("proof1")
690                        .return_to_worktop("bucket")
691                        .add_raw_instruction_ignoring_all_side_effects(CloneProof { proof_id })
692                })
693                .build_no_validate(),
694        );
695        let validator = TransactionValidator::new_for_latest_simulator();
696        assert_matches!(
697            transaction.prepare_and_validate(&validator),
698            Err(TransactionValidationError::IntentValidationError(
699                _,
700                IntentValidationError::ManifestValidationError(
701                    ManifestValidationError::ProofAlreadyUsed(ManifestProof(0), _,)
702                )
703            ))
704        );
705    }
706
707    #[test]
708    fn verify_call_direct_method_args_are_processed() {
709        let transaction = create_transaction_advanced(
710            Epoch::of(0),
711            Epoch::of(40),
712            123,
713            vec![55],
714            66,
715            ManifestBuilder::new()
716                .take_from_worktop(XRD, dec!(100), "bucket")
717                .then(|builder| {
718                    let lookup = builder.name_lookup();
719                    builder
720                        .call_direct_access_method(
721                            InternalAddress::new_or_panic(
722                                [EntityType::InternalFungibleVault as u8; NodeId::LENGTH],
723                            ),
724                            "test",
725                            manifest_args!(lookup.bucket("bucket")),
726                        )
727                        .return_to_worktop("bucket")
728                })
729                .build_no_validate(),
730        );
731        let validator = TransactionValidator::new_for_latest_simulator();
732        assert_matches!(
733            transaction.prepare_and_validate(&validator),
734            Err(TransactionValidationError::IntentValidationError(
735                _,
736                IntentValidationError::ManifestValidationError(
737                    ManifestValidationError::BucketAlreadyUsed(ManifestBucket(0), _,)
738                )
739            ))
740        );
741    }
742}