radix_transactions/manifest/
static_manifest_interpreter.rs

1use crate::internal_prelude::*;
2use core::ops::ControlFlow;
3
4use traversal::*;
5use ManifestInstructionEffect as Effect;
6
7/// This is a manifest interpreter which can be used to easily perform
8/// more complex validations on a manifest, and supports an optional
9/// visitor plugin mechanism.
10///
11/// This forms a next-generation validation ahead of the [`BasicManifestValidator`].
12pub struct StaticManifestInterpreter<'a, M: ReadableManifest + ?Sized> {
13    validation_ruleset: ValidationRuleset,
14    manifest: &'a M,
15    location: ManifestLocation,
16    registered_blobs: IndexSet<ManifestBlobRef>,
17    bucket_state: Vec<BucketState<'a>>,
18    proof_state: Vec<ProofState<'a>>,
19    address_reservation_state: Vec<AddressReservationState<'a>>,
20    named_address_state: Vec<NamedAddressState<'a>>,
21    intent_state: Vec<IntentState<'a>>,
22    next_instruction_requirement: NextInstructionRequirement,
23}
24
25// --------------------------------------------
26// IMPLEMENTATION NOTES - Regarding ControlFlow
27// --------------------------------------------
28// This manifest interpreter uses an optional visitor pattern, with the
29// ControlFlow element from the Rust core library.
30//
31// ControlFlow is designed for a visitor use case, but as per my comment here
32// (https://github.com/rust-lang/rust/issues/75744#issuecomment-2358375882)
33// there are a couple of key missing functions:
34// * It is missing #[must_use] - which means it's very easy to miss a ? in
35//   an intermediate layer. As a workaround, we should stick #[must_use] on
36//   all methods returning it.
37//   (... yes, there is a war story here where I wasted more time than I care
38//   to admit debugging a test :facepalm:)
39// * It is missing a built-in conversion to Result
40// * It is missing an automatic from conversion on Break when using the ?
41//   operator. Apparently this is desired.
42//
43// Perhaps we should consider using Result here as it'd be easier to work with,
44// even if semantically less accurate.
45// --------------------------------------------
46
47impl<'a, M: ReadableManifest + ?Sized> StaticManifestInterpreter<'a, M> {
48    pub fn new(validation_ruleset: ValidationRuleset, manifest: &'a M) -> Self {
49        Self {
50            validation_ruleset,
51            manifest,
52            location: ManifestLocation::Preamble,
53            registered_blobs: Default::default(),
54            bucket_state: Default::default(),
55            proof_state: Default::default(),
56            address_reservation_state: Default::default(),
57            named_address_state: Default::default(),
58            intent_state: Default::default(),
59            next_instruction_requirement: NextInstructionRequirement::None,
60        }
61    }
62
63    pub fn validate(self) -> Result<(), ManifestValidationError> {
64        self.validate_and_apply_visitor(&mut ())
65    }
66
67    pub fn validate_and_apply_visitor<V: ManifestInterpretationVisitor>(
68        self,
69        visitor: &mut V,
70    ) -> Result<(), V::Output> {
71        // For some reason ControlFlow doesn't implement Into<Result>
72        match self.interpret_internal(visitor) {
73            ControlFlow::Continue(()) => Ok(()),
74            ControlFlow::Break(err) => Err(err),
75        }
76    }
77
78    #[must_use]
79    fn interpret_internal<V: ManifestInterpretationVisitor>(
80        mut self,
81        visitor: &mut V,
82    ) -> ControlFlow<V::Output> {
83        self.handle_preallocated_addresses(visitor, self.manifest.get_preallocated_addresses())?;
84        self.handle_child_subintents(visitor, self.manifest.get_child_subintent_hashes())?;
85        self.handle_blobs(visitor, self.manifest.get_blobs())?;
86        for (index, instruction_effect) in self.manifest.iter_instruction_effects().enumerate() {
87            self.handle_instruction(visitor, index, instruction_effect)?;
88        }
89        self.verify_final_instruction::<V>()?;
90        self.handle_wrap_up(visitor)?;
91        ControlFlow::Continue(())
92    }
93
94    #[must_use]
95    fn handle_preallocated_addresses<V: ManifestInterpretationVisitor>(
96        &mut self,
97        visitor: &mut V,
98        preallocated_addresses: &'a [PreAllocatedAddress],
99    ) -> ControlFlow<V::Output> {
100        for preallocated_address in preallocated_addresses.iter() {
101            let _ = self.handle_new_address_reservation(
102                visitor,
103                &preallocated_address.blueprint_id.package_address,
104                preallocated_address.blueprint_id.blueprint_name.as_str(),
105                Some(&preallocated_address.address),
106            )?;
107        }
108        ControlFlow::Continue(())
109    }
110
111    #[must_use]
112    fn handle_child_subintents<V: ManifestInterpretationVisitor>(
113        &mut self,
114        visitor: &mut V,
115        child_subintents: impl Iterator<Item = &'a ChildSubintentSpecifier>,
116    ) -> ControlFlow<V::Output> {
117        for child_subintent in child_subintents {
118            self.handle_new_intent(
119                visitor,
120                IntentHash::Subintent(child_subintent.hash),
121                IntentType::Child,
122            )?;
123        }
124        ControlFlow::Continue(())
125    }
126
127    #[must_use]
128    fn handle_blobs<'b, V: ManifestInterpretationVisitor>(
129        &mut self,
130        visitor: &mut V,
131        blobs: impl Iterator<Item = (&'b Hash, &'b Vec<u8>)>,
132    ) -> ControlFlow<V::Output> {
133        for (hash, content) in blobs {
134            if !self.registered_blobs.insert(ManifestBlobRef(hash.0)) {
135                if self.validation_ruleset.validate_no_duplicate_blobs {
136                    return ControlFlow::Break(
137                        ManifestValidationError::DuplicateBlob(ManifestBlobRef(hash.0)).into(),
138                    );
139                }
140            }
141            visitor.on_register_blob(OnRegisterBlob {
142                blob_ref: ManifestBlobRef(hash.0),
143                content: content.as_ref(),
144            })?;
145        }
146        ControlFlow::Continue(())
147    }
148
149    #[must_use]
150    fn handle_instruction<V: ManifestInterpretationVisitor>(
151        &mut self,
152        visitor: &mut V,
153        index: usize,
154        effect: ManifestInstructionEffect<'a>,
155    ) -> ControlFlow<V::Output> {
156        self.location = ManifestLocation::Instruction { index };
157
158        match self
159            .next_instruction_requirement
160            .handle_next_instruction(effect)
161        {
162            Ok(()) => {}
163            Err(error) => return ControlFlow::Break(error.into()),
164        }
165
166        visitor.on_start_instruction(OnStartInstruction { index })?;
167
168        match effect {
169            Effect::CreateBucket { source_amount } => {
170                self.handle_new_bucket(visitor, source_amount)?;
171            }
172            Effect::CreateProof { source_amount } => {
173                self.handle_new_proof(visitor, source_amount)?;
174            }
175            Effect::ConsumeBucket {
176                consumed_bucket,
177                destination,
178            } => {
179                self.consume_bucket(visitor, consumed_bucket, destination)?;
180            }
181            Effect::ConsumeProof {
182                consumed_proof,
183                destination,
184            } => {
185                self.consume_proof(visitor, consumed_proof, destination)?;
186            }
187            Effect::CloneProof { cloned_proof } => {
188                self.handle_cloned_proof(visitor, cloned_proof)?;
189            }
190            Effect::DropManyProofs {
191                drop_all_named_proofs,
192                drop_all_authzone_signature_proofs,
193                drop_all_authzone_non_signature_proofs,
194            } => {
195                if drop_all_named_proofs {
196                    let proofs_to_drop: Vec<_> = self
197                        .proof_state
198                        .iter()
199                        .enumerate()
200                        .filter_map(|(index, p)| match p.consumed_at {
201                            Some(_) => None,
202                            None => Some(ManifestProof(index as u32)),
203                        })
204                        .collect();
205                    for proof in proofs_to_drop {
206                        self.consume_proof(visitor, proof, ProofDestination::Drop)?;
207                    }
208                }
209                if drop_all_authzone_signature_proofs || drop_all_authzone_non_signature_proofs {
210                    visitor.on_drop_authzone_proofs(OnDropAuthZoneProofs {
211                        drop_all_signature_proofs: drop_all_authzone_signature_proofs,
212                        drop_all_non_signature_proofs: drop_all_authzone_non_signature_proofs,
213                    })?;
214                }
215            }
216            Effect::Invocation { kind, args } => {
217                self.handle_invocation(visitor, kind, args)?;
218            }
219            Effect::CreateAddressAndReservation {
220                package_address,
221                blueprint_name,
222            } => {
223                let reservation = self.handle_new_address_reservation(
224                    visitor,
225                    package_address,
226                    blueprint_name,
227                    None,
228                )?;
229                self.handle_new_named_address(
230                    visitor,
231                    Some(reservation),
232                    package_address,
233                    blueprint_name,
234                )?;
235            }
236            Effect::ResourceAssertion { assertion } => {
237                self.handle_resource_assertion(visitor, assertion)?;
238            }
239            Effect::Verification {
240                verification,
241                access_rule,
242            } => {
243                self.handle_verification(visitor, verification, access_rule)?;
244            }
245        }
246
247        visitor.on_end_instruction(OnEndInstruction { index, effect })
248    }
249
250    #[must_use]
251    fn verify_final_instruction<V: ManifestInterpretationVisitor>(
252        &mut self,
253    ) -> ControlFlow<V::Output> {
254        if !self.manifest.is_subintent() {
255            return ControlFlow::Continue(());
256        }
257        let instruction_count = self.manifest.instruction_count();
258        let last_instruction_index = if instruction_count > 0 {
259            instruction_count - 1
260        } else {
261            return ControlFlow::Break(
262                ManifestValidationError::SubintentDoesNotEndWithYieldToParent.into(),
263            );
264        };
265        match self.manifest.instruction_effect(last_instruction_index) {
266            ManifestInstructionEffect::Invocation {
267                kind: InvocationKind::YieldToParent,
268                ..
269            } => ControlFlow::Continue(()),
270            _ => ControlFlow::Break(
271                ManifestValidationError::SubintentDoesNotEndWithYieldToParent.into(),
272            ),
273        }
274    }
275
276    #[must_use]
277    fn handle_wrap_up<V: ManifestInterpretationVisitor>(
278        &mut self,
279        visitor: &mut V,
280    ) -> ControlFlow<V::Output> {
281        match self.next_instruction_requirement.validate_at_end() {
282            Ok(()) => {}
283            Err(error) => return ControlFlow::Break(error.into()),
284        }
285        if self.validation_ruleset.validate_no_dangling_nodes {
286            for (index, state) in self.bucket_state.iter().enumerate() {
287                if state.consumed_at.is_none() {
288                    return ControlFlow::Break(
289                        ManifestValidationError::DanglingBucket(
290                            ManifestBucket(index as u32),
291                            format!("{state:?}"),
292                        )
293                        .into(),
294                    );
295                }
296            }
297            for (index, state) in self.address_reservation_state.iter().enumerate() {
298                if state.consumed_at.is_none() {
299                    return ControlFlow::Break(
300                        ManifestValidationError::DanglingAddressReservation(
301                            ManifestAddressReservation(index as u32),
302                            format!("{state:?}"),
303                        )
304                        .into(),
305                    );
306                }
307            }
308        }
309        visitor.on_finish(OnFinish)?;
310
311        ControlFlow::Continue(())
312    }
313
314    #[must_use]
315    fn handle_invocation<V: ManifestInterpretationVisitor>(
316        &mut self,
317        visitor: &mut V,
318        invocation_kind: InvocationKind<'a>,
319        args: &'a ManifestValue,
320    ) -> ControlFlow<V::Output> {
321        let yields_across_intent = match invocation_kind {
322            InvocationKind::Method { address, .. } => {
323                if self
324                    .validation_ruleset
325                    .validate_dynamic_address_in_command_part
326                {
327                    match address {
328                        ManifestGlobalAddress::Static(_) => {}
329                        ManifestGlobalAddress::Named(named_address) => {
330                            // Check it exists
331                            self.get_existing_named_address::<V>(*named_address)?;
332                        }
333                    }
334                }
335                false
336            }
337            InvocationKind::Function { address, .. } => {
338                if self
339                    .validation_ruleset
340                    .validate_dynamic_address_in_command_part
341                {
342                    match address {
343                        ManifestPackageAddress::Static(_) => {}
344                        ManifestPackageAddress::Named(named_address) => {
345                            // Check it exists
346                            self.get_existing_named_address::<V>(*named_address)?;
347                        }
348                    }
349                }
350                false
351            }
352            InvocationKind::DirectMethod { .. } => false,
353            InvocationKind::YieldToParent => {
354                if !self.manifest.is_subintent() {
355                    return ControlFlow::Break(
356                        ManifestValidationError::InstructionNotSupportedInTransactionIntent.into(),
357                    );
358                }
359                true
360            }
361            InvocationKind::YieldToChild { child_index } => {
362                let index = child_index.0 as usize;
363                if index >= self.manifest.get_child_subintent_hashes().len() {
364                    return ControlFlow::Break(
365                        ManifestValidationError::ChildIntentNotRegistered(child_index).into(),
366                    );
367                }
368                true
369            }
370        };
371        let encoded = match manifest_encode(args) {
372            Ok(encoded) => encoded,
373            Err(error) => {
374                return ControlFlow::Break(ManifestValidationError::ArgsEncodeError(error).into())
375            }
376        };
377        let mut traverser = ManifestTraverser::new(
378            &encoded,
379            ExpectedStart::PayloadPrefix(MANIFEST_SBOR_V1_PAYLOAD_PREFIX),
380            VecTraverserConfig {
381                max_depth: MANIFEST_SBOR_V1_MAX_DEPTH,
382                check_exact_end: true,
383            },
384        );
385        loop {
386            let event = traverser.next_event();
387            match event.event {
388                TraversalEvent::ContainerStart(_) => {}
389                TraversalEvent::ContainerEnd(_) => {}
390                TraversalEvent::TerminalValue(r) => {
391                    if let traversal::TerminalValueRef::Custom(c) = r {
392                        match c.0 {
393                            ManifestCustomValue::Address(address) => {
394                                match address {
395                                    ManifestAddress::Static(_) => {}
396                                    ManifestAddress::Named(named_address) => {
397                                        // Check it exists
398                                        self.get_existing_named_address::<V>(named_address)?;
399                                    }
400                                }
401                            }
402                            ManifestCustomValue::Bucket(bucket) => {
403                                self.consume_bucket(
404                                    visitor,
405                                    bucket,
406                                    BucketDestination::Invocation(invocation_kind),
407                                )?;
408                            }
409                            ManifestCustomValue::Proof(proof) => {
410                                if yields_across_intent {
411                                    return ControlFlow::Break(
412                                        ManifestValidationError::ProofCannotBePassedToAnotherIntent
413                                            .into(),
414                                    );
415                                }
416                                self.consume_proof(
417                                    visitor,
418                                    proof,
419                                    ProofDestination::Invocation(invocation_kind),
420                                )?;
421                            }
422                            ManifestCustomValue::Expression(expression) => {
423                                visitor.on_pass_expression(OnPassExpression {
424                                    expression,
425                                    destination: ExpressionDestination::Invocation(invocation_kind),
426                                })?;
427                            }
428                            ManifestCustomValue::Blob(blob_ref) => {
429                                if self.validation_ruleset.validate_blob_refs {
430                                    if !self.registered_blobs.contains(&blob_ref) {
431                                        return ControlFlow::Break(
432                                            ManifestValidationError::BlobNotRegistered(blob_ref)
433                                                .into(),
434                                        );
435                                    }
436                                }
437                                visitor.on_pass_blob(OnPassBlob {
438                                    blob_ref,
439                                    destination: BlobDestination::Invocation(invocation_kind),
440                                })?;
441                            }
442                            ManifestCustomValue::AddressReservation(reservation) => {
443                                self.consume_address_reservation(
444                                    visitor,
445                                    reservation,
446                                    AddressReservationDestination::Invocation(invocation_kind),
447                                )?;
448                            }
449                            ManifestCustomValue::Decimal(_)
450                            | ManifestCustomValue::NonFungibleLocalId(_)
451                            | ManifestCustomValue::PreciseDecimal(_) => {}
452                        }
453                    }
454                }
455                TraversalEvent::TerminalValueBatch(_) => {}
456                TraversalEvent::End => {
457                    break;
458                }
459                TraversalEvent::DecodeError(error) => {
460                    return ControlFlow::Break(
461                        ManifestValidationError::ArgsDecodeError(error).into(),
462                    );
463                }
464            }
465        }
466        ControlFlow::Continue(())
467    }
468
469    #[must_use]
470    fn handle_resource_assertion<V: ManifestInterpretationVisitor>(
471        &mut self,
472        visitor: &mut V,
473        assertion: ResourceAssertion<'a>,
474    ) -> ControlFlow<V::Output> {
475        if self.validation_ruleset.validate_resource_assertions {
476            match assertion {
477                ResourceAssertion::Worktop(WorktopAssertion::ResourceNonZeroAmount { .. }) => {
478                    // Nothing to validate
479                }
480                ResourceAssertion::Worktop(WorktopAssertion::ResourceAtLeastAmount {
481                    amount,
482                    ..
483                }) => {
484                    if amount.is_negative() {
485                        return ControlFlow::Break(
486                            ManifestValidationError::InvalidResourceConstraint.into(),
487                        );
488                    }
489                }
490                ResourceAssertion::Worktop(WorktopAssertion::ResourceAtLeastNonFungibles {
491                    resource_address,
492                    ..
493                }) => {
494                    if resource_address.is_fungible() {
495                        return ControlFlow::Break(
496                            ManifestValidationError::InvalidResourceConstraint.into(),
497                        );
498                    }
499                }
500                ResourceAssertion::Worktop(WorktopAssertion::ResourcesOnly { constraints })
501                | ResourceAssertion::Worktop(WorktopAssertion::ResourcesInclude { constraints }) => {
502                    if !constraints.is_valid() {
503                        return ControlFlow::Break(
504                            ManifestValidationError::InvalidResourceConstraint.into(),
505                        );
506                    }
507                }
508                ResourceAssertion::NextCall(NextCallAssertion::ReturnsOnly { constraints })
509                | ResourceAssertion::NextCall(NextCallAssertion::ReturnsInclude { constraints }) => {
510                    if !constraints.is_valid() {
511                        return ControlFlow::Break(
512                            ManifestValidationError::InvalidResourceConstraint.into(),
513                        );
514                    }
515                    self.next_instruction_requirement =
516                        NextInstructionRequirement::RequiredInvocationDueToNextCallAssertion;
517                }
518                ResourceAssertion::Bucket(BucketAssertion::Contents { bucket, constraint }) => {
519                    // Check the bucket currently exists
520                    let state = self.get_existing_bucket::<V>(bucket)?;
521                    let resource_address = state.source_amount.resource_address();
522                    if !constraint.is_valid_for(resource_address) {
523                        return ControlFlow::Break(
524                            ManifestValidationError::InvalidResourceConstraint.into(),
525                        );
526                    }
527                }
528            }
529        }
530        visitor.on_resource_assertion(OnResourceAssertion { assertion })
531    }
532
533    #[must_use]
534    fn handle_verification<V: ManifestInterpretationVisitor>(
535        &mut self,
536        visitor: &mut V,
537        verification_kind: VerificationKind,
538        access_rule: &AccessRule,
539    ) -> ControlFlow<V::Output> {
540        match verification_kind {
541            VerificationKind::Parent => {
542                if !self.manifest.is_subintent() {
543                    return ControlFlow::Break(
544                        ManifestValidationError::InstructionNotSupportedInTransactionIntent.into(),
545                    );
546                }
547            }
548        }
549        visitor.on_verification(OnVerification {
550            kind: verification_kind,
551            access_rule,
552        })?;
553        ControlFlow::Continue(())
554    }
555
556    #[must_use]
557    fn handle_new_bucket<V: ManifestInterpretationVisitor>(
558        &mut self,
559        visitor: &mut V,
560        source_amount: BucketSourceAmount<'a>,
561    ) -> ControlFlow<V::Output> {
562        let new_bucket = ManifestBucket(self.bucket_state.len() as u32);
563        let state = BucketState {
564            name: self
565                .manifest
566                .get_known_object_names_ref()
567                .known_bucket_name(new_bucket),
568            created_at: self.location,
569            proof_locks: 0,
570            consumed_at: None,
571            source_amount,
572        };
573        visitor.on_new_bucket(OnNewBucket {
574            bucket: new_bucket,
575            state: &state,
576        })?;
577        self.bucket_state.push(state);
578        ControlFlow::Continue(())
579    }
580
581    #[must_use]
582    fn get_existing_bucket<V: ManifestInterpretationVisitor>(
583        &mut self,
584        bucket: ManifestBucket,
585    ) -> ControlFlow<V::Output, &mut BucketState<'a>> {
586        match self.bucket_state.get_mut(bucket.0 as usize) {
587            Some(state) => {
588                if state.consumed_at.is_some() {
589                    ControlFlow::Break(
590                        ManifestValidationError::BucketAlreadyUsed(bucket, format!("{state:?}"))
591                            .into(),
592                    )
593                } else {
594                    ControlFlow::Continue(state)
595                }
596            }
597            None => ControlFlow::Break(ManifestValidationError::BucketNotYetCreated(bucket).into()),
598        }
599    }
600
601    #[must_use]
602    fn consume_bucket<V: ManifestInterpretationVisitor>(
603        &mut self,
604        visitor: &mut V,
605        bucket: ManifestBucket,
606        destination: BucketDestination<'a>,
607    ) -> ControlFlow<V::Output> {
608        let check_proof_locks = self.validation_ruleset.validate_bucket_proof_lock;
609        let location = self.location;
610        let state = self.get_existing_bucket::<V>(bucket)?;
611        if check_proof_locks && state.proof_locks > 0 {
612            return ControlFlow::Break(
613                ManifestValidationError::BucketConsumedWhilstLockedByProof(
614                    bucket,
615                    format!("{state:?}"),
616                )
617                .into(),
618            );
619        }
620        state.consumed_at = Some(location);
621        visitor.on_consume_bucket(OnConsumeBucket {
622            bucket,
623            state: &state,
624            destination,
625        })
626    }
627
628    #[must_use]
629    fn handle_new_proof<V: ManifestInterpretationVisitor>(
630        &mut self,
631        visitor: &mut V,
632        source_amount: ProofSourceAmount<'a>,
633    ) -> ControlFlow<V::Output> {
634        match source_amount.proof_kind() {
635            ProofKind::BucketProof(bucket) => {
636                self.get_existing_bucket::<V>(bucket)?.proof_locks += 1;
637            }
638            ProofKind::AuthZoneProof => {}
639        }
640        let new_proof = ManifestProof(self.proof_state.len() as u32);
641        let state = ProofState {
642            name: self
643                .manifest
644                .get_known_object_names_ref()
645                .known_proof_name(new_proof),
646            created_at: self.location,
647            consumed_at: None,
648            source_amount,
649        };
650        visitor.on_new_proof(OnNewProof {
651            proof: new_proof,
652            state: &state,
653        })?;
654        self.proof_state.push(state);
655        ControlFlow::Continue(())
656    }
657
658    #[must_use]
659    fn get_existing_proof<V: ManifestInterpretationVisitor>(
660        &mut self,
661        proof: ManifestProof,
662    ) -> ControlFlow<V::Output, &mut ProofState<'a>> {
663        match self.proof_state.get_mut(proof.0 as usize) {
664            Some(state) => {
665                if state.consumed_at.is_some() {
666                    ControlFlow::Break(
667                        ManifestValidationError::ProofAlreadyUsed(proof, format!("{state:?}"))
668                            .into(),
669                    )
670                } else {
671                    ControlFlow::Continue(state)
672                }
673            }
674            None => ControlFlow::Break(ManifestValidationError::ProofNotYetCreated(proof).into()),
675        }
676    }
677
678    #[must_use]
679    fn handle_cloned_proof<V: ManifestInterpretationVisitor>(
680        &mut self,
681        visitor: &mut V,
682        cloned_proof: ManifestProof,
683    ) -> ControlFlow<V::Output> {
684        let source_amount = self.get_existing_proof::<V>(cloned_proof)?.source_amount;
685        self.handle_new_proof(visitor, source_amount)
686    }
687
688    #[must_use]
689    fn consume_proof<V: ManifestInterpretationVisitor>(
690        &mut self,
691        visitor: &mut V,
692        proof: ManifestProof,
693        destination: ProofDestination<'a>,
694    ) -> ControlFlow<V::Output> {
695        let location = self.location;
696        let state = self.get_existing_proof::<V>(proof)?;
697        state.consumed_at = Some(location);
698        visitor.on_consume_proof(OnConsumeProof {
699            proof,
700            state: &state,
701            destination,
702        })?;
703        let source_amount = state.source_amount;
704        match source_amount.proof_kind() {
705            ProofKind::BucketProof(bucket) => {
706                self.get_existing_bucket::<V>(bucket)?.proof_locks -= 1;
707            }
708            ProofKind::AuthZoneProof => {}
709        }
710        ControlFlow::Continue(())
711    }
712
713    #[must_use]
714    fn handle_new_address_reservation<V: ManifestInterpretationVisitor>(
715        &mut self,
716        visitor: &mut V,
717        package_address: &'a PackageAddress,
718        blueprint_name: &'a str,
719        preallocated_address: Option<&'a GlobalAddress>,
720    ) -> ControlFlow<V::Output, ManifestAddressReservation> {
721        let new_address_reservation =
722            ManifestAddressReservation(self.address_reservation_state.len() as u32);
723        let state = AddressReservationState {
724            name: self
725                .manifest
726                .get_known_object_names_ref()
727                .known_address_reservation_name(new_address_reservation),
728            package_address,
729            blueprint_name,
730            preallocated_address,
731            created_at: self.location,
732            consumed_at: None,
733        };
734        visitor.on_new_address_reservation(OnNewAddressReservation {
735            address_reservation: new_address_reservation,
736            state: &state,
737        })?;
738        self.address_reservation_state.push(state);
739        ControlFlow::Continue(new_address_reservation)
740    }
741
742    #[must_use]
743    fn get_existing_address_reservation<V: ManifestInterpretationVisitor>(
744        &mut self,
745        address_reservation: ManifestAddressReservation,
746    ) -> ControlFlow<V::Output, &mut AddressReservationState<'a>> {
747        match self
748            .address_reservation_state
749            .get_mut(address_reservation.0 as usize)
750        {
751            Some(state) => {
752                if state.consumed_at.is_some() {
753                    ControlFlow::Break(
754                        ManifestValidationError::AddressReservationAlreadyUsed(
755                            address_reservation,
756                            format!("{state:?}"),
757                        )
758                        .into(),
759                    )
760                } else {
761                    ControlFlow::Continue(state)
762                }
763            }
764            None => ControlFlow::Break(
765                ManifestValidationError::AddressReservationNotYetCreated(address_reservation)
766                    .into(),
767            ),
768        }
769    }
770
771    #[must_use]
772    fn consume_address_reservation<V: ManifestInterpretationVisitor>(
773        &mut self,
774        visitor: &mut V,
775        address_reservation: ManifestAddressReservation,
776        destination: AddressReservationDestination<'a>,
777    ) -> ControlFlow<V::Output> {
778        let location = self.location;
779        let state = self.get_existing_address_reservation::<V>(address_reservation)?;
780        state.consumed_at = Some(location);
781        visitor.on_consume_address_reservation(OnConsumeAddressReservation {
782            address_reservation,
783            state: &state,
784            destination,
785        })
786    }
787
788    #[must_use]
789    fn handle_new_named_address<V: ManifestInterpretationVisitor>(
790        &mut self,
791        visitor: &mut V,
792        associated_reservation: Option<ManifestAddressReservation>,
793        package_address: &PackageAddress,
794        blueprint_name: &str,
795    ) -> ControlFlow<V::Output> {
796        let new_named_address = ManifestNamedAddress(self.named_address_state.len() as u32);
797        let state = NamedAddressState {
798            name: self
799                .manifest
800                .get_known_object_names_ref()
801                .known_address_name(new_named_address),
802            associated_reservation,
803            created_at: self.location,
804        };
805        visitor.on_new_named_address(OnNewNamedAddress {
806            named_address: new_named_address,
807            state: &state,
808            package_address,
809            blueprint_name,
810        })?;
811        self.named_address_state.push(state);
812        ControlFlow::Continue(())
813    }
814
815    #[must_use]
816    fn get_existing_named_address<V: ManifestInterpretationVisitor>(
817        &mut self,
818        named_address: ManifestNamedAddress,
819    ) -> ControlFlow<V::Output, &mut NamedAddressState<'a>> {
820        match self.named_address_state.get_mut(named_address.0 as usize) {
821            Some(state) => ControlFlow::Continue(state),
822            None => ControlFlow::Break(
823                ManifestValidationError::NamedAddressNotYetCreated(named_address).into(),
824            ),
825        }
826    }
827
828    #[must_use]
829    fn handle_new_intent<V: ManifestInterpretationVisitor>(
830        &mut self,
831        visitor: &mut V,
832        intent_hash: IntentHash,
833        intent_type: IntentType,
834    ) -> ControlFlow<V::Output> {
835        let new_intent = ManifestNamedIntent(self.intent_state.len() as u32);
836        let state = IntentState {
837            name: self
838                .manifest
839                .get_known_object_names_ref()
840                .known_intent_name(new_intent),
841            intent_hash,
842            intent_type,
843            created_at: self.location,
844        };
845        visitor.on_new_intent(OnNewIntent {
846            intent: new_intent,
847            state: &state,
848        })?;
849        self.intent_state.push(state);
850        ControlFlow::Continue(())
851    }
852}
853
854#[derive(Debug, Copy, Clone, PartialEq, Eq)]
855pub enum ManifestLocation {
856    Preamble,
857    Instruction { index: usize },
858}
859
860#[derive(Debug, Clone, PartialEq, Eq)]
861pub struct BucketState<'a> {
862    pub name: Option<&'a str>,
863    pub source_amount: BucketSourceAmount<'a>,
864    pub created_at: ManifestLocation,
865    pub proof_locks: u32,
866    pub consumed_at: Option<ManifestLocation>,
867}
868
869#[derive(Debug, Clone, PartialEq, Eq)]
870pub struct ProofState<'a> {
871    pub name: Option<&'a str>,
872    pub source_amount: ProofSourceAmount<'a>,
873    pub created_at: ManifestLocation,
874    pub consumed_at: Option<ManifestLocation>,
875}
876
877#[derive(Debug, Clone, PartialEq, Eq)]
878pub struct AddressReservationState<'a> {
879    pub name: Option<&'a str>,
880    pub package_address: &'a PackageAddress,
881    pub blueprint_name: &'a str,
882    pub preallocated_address: Option<&'a GlobalAddress>,
883    pub created_at: ManifestLocation,
884    pub consumed_at: Option<ManifestLocation>,
885}
886
887#[derive(Debug, Clone, PartialEq, Eq)]
888pub struct NamedAddressState<'a> {
889    pub name: Option<&'a str>,
890    pub associated_reservation: Option<ManifestAddressReservation>,
891    pub created_at: ManifestLocation,
892}
893
894#[derive(Debug, Clone, PartialEq, Eq)]
895pub struct IntentState<'a> {
896    pub name: Option<&'a str>,
897    pub intent_hash: IntentHash,
898    pub intent_type: IntentType,
899    pub created_at: ManifestLocation,
900}
901
902#[derive(Debug, Clone, PartialEq, Eq)]
903pub enum IntentType {
904    Child,
905}
906
907enum NextInstructionRequirement {
908    None,
909    RequiredInvocationDueToNextCallAssertion,
910}
911
912impl NextInstructionRequirement {
913    fn handle_next_instruction(
914        &mut self,
915        effect: ManifestInstructionEffect,
916    ) -> Result<(), ManifestValidationError> {
917        match self {
918            NextInstructionRequirement::None => Ok(()),
919            NextInstructionRequirement::RequiredInvocationDueToNextCallAssertion => {
920                if matches!(effect, ManifestInstructionEffect::Invocation { .. }) {
921                    *self = NextInstructionRequirement::None;
922                    Ok(())
923                } else {
924                    Err(ManifestValidationError::InstructionFollowingNextCallAssertionWasNotInvocation)
925                }
926            }
927        }
928    }
929
930    fn validate_at_end(&self) -> Result<(), ManifestValidationError> {
931        match self {
932            NextInstructionRequirement::None => Ok(()),
933            NextInstructionRequirement::RequiredInvocationDueToNextCallAssertion => {
934                Err(ManifestValidationError::ManifestEndedWhilstExpectingNextCallAssertion)
935            }
936        }
937    }
938}
939
940// TODO can add:
941// * validate_preallocated_address_against_blueprint
942// ...
943// Possibly we should consider making this a generic to make it more performant.
944pub struct ValidationRuleset {
945    pub validate_no_duplicate_blobs: bool,
946    pub validate_blob_refs: bool,
947    pub validate_bucket_proof_lock: bool,
948    pub validate_no_dangling_nodes: bool,
949    pub validate_dynamic_address_in_command_part: bool,
950    pub validate_resource_assertions: bool,
951}
952
953impl Default for ValidationRuleset {
954    fn default() -> Self {
955        Self::all()
956    }
957}
958
959#[derive(Debug, Copy, Clone, PartialEq, Eq, Sbor)]
960pub enum InterpreterValidationRulesetSpecifier {
961    AllValidations,
962    Cuttlefish,
963}
964
965impl ValidationRuleset {
966    pub fn for_specifier(specifier: InterpreterValidationRulesetSpecifier) -> Self {
967        match specifier {
968            InterpreterValidationRulesetSpecifier::AllValidations => Self::all(),
969            InterpreterValidationRulesetSpecifier::Cuttlefish => Self::cuttlefish(),
970        }
971    }
972
973    pub fn all() -> Self {
974        Self {
975            validate_no_duplicate_blobs: true,
976            validate_blob_refs: true,
977            validate_bucket_proof_lock: true,
978            validate_no_dangling_nodes: true,
979            validate_dynamic_address_in_command_part: true,
980            validate_resource_assertions: true,
981        }
982    }
983
984    pub fn babylon_equivalent() -> Self {
985        Self {
986            validate_no_duplicate_blobs: false,
987            validate_blob_refs: false,
988            validate_bucket_proof_lock: true,
989            validate_no_dangling_nodes: false,
990            validate_dynamic_address_in_command_part: false,
991            validate_resource_assertions: false,
992        }
993    }
994
995    pub fn cuttlefish() -> Self {
996        Self {
997            validate_no_duplicate_blobs: true,
998            validate_blob_refs: true,
999            validate_bucket_proof_lock: true,
1000            validate_no_dangling_nodes: true,
1001            validate_dynamic_address_in_command_part: true,
1002            validate_resource_assertions: true,
1003        }
1004    }
1005}
1006
1007#[derive(Debug, Clone, PartialEq, Eq)]
1008pub enum ManifestValidationError {
1009    DuplicateBlob(ManifestBlobRef),
1010    BlobNotRegistered(ManifestBlobRef),
1011    BucketNotYetCreated(ManifestBucket),
1012    BucketAlreadyUsed(ManifestBucket, String),
1013    BucketConsumedWhilstLockedByProof(ManifestBucket, String),
1014    ProofNotYetCreated(ManifestProof),
1015    ProofAlreadyUsed(ManifestProof, String),
1016    AddressReservationNotYetCreated(ManifestAddressReservation),
1017    AddressReservationAlreadyUsed(ManifestAddressReservation, String),
1018    NamedAddressNotYetCreated(ManifestNamedAddress),
1019    ChildIntentNotRegistered(ManifestNamedIntent),
1020    DanglingBucket(ManifestBucket, String),
1021    DanglingAddressReservation(ManifestAddressReservation, String),
1022    ArgsEncodeError(EncodeError),
1023    ArgsDecodeError(DecodeError),
1024    InstructionNotSupportedInTransactionIntent,
1025    SubintentDoesNotEndWithYieldToParent,
1026    ProofCannotBePassedToAnotherIntent,
1027    TooManyInstructions,
1028    InvalidResourceConstraint,
1029    InstructionFollowingNextCallAssertionWasNotInvocation,
1030    ManifestEndedWhilstExpectingNextCallAssertion,
1031}
1032
1033// We allow unused variables so we don't have to prefix them all with `_`
1034#[allow(unused_variables)]
1035pub trait ManifestInterpretationVisitor {
1036    type Output: From<ManifestValidationError>;
1037
1038    #[must_use]
1039    fn on_start_instruction(&mut self, details: OnStartInstruction) -> ControlFlow<Self::Output> {
1040        ControlFlow::Continue(())
1041    }
1042
1043    #[must_use]
1044    fn on_end_instruction(&mut self, details: OnEndInstruction) -> ControlFlow<Self::Output> {
1045        ControlFlow::Continue(())
1046    }
1047
1048    #[must_use]
1049    fn on_new_bucket(&mut self, details: OnNewBucket) -> ControlFlow<Self::Output> {
1050        ControlFlow::Continue(())
1051    }
1052
1053    #[must_use]
1054    fn on_consume_bucket(&mut self, details: OnConsumeBucket) -> ControlFlow<Self::Output> {
1055        ControlFlow::Continue(())
1056    }
1057
1058    #[must_use]
1059    fn on_new_proof(&mut self, details: OnNewProof) -> ControlFlow<Self::Output> {
1060        ControlFlow::Continue(())
1061    }
1062
1063    #[must_use]
1064    fn on_consume_proof(&mut self, details: OnConsumeProof) -> ControlFlow<Self::Output> {
1065        ControlFlow::Continue(())
1066    }
1067
1068    #[must_use]
1069    fn on_new_address_reservation(
1070        &mut self,
1071        details: OnNewAddressReservation,
1072    ) -> ControlFlow<Self::Output> {
1073        ControlFlow::Continue(())
1074    }
1075
1076    #[must_use]
1077    fn on_consume_address_reservation(
1078        &mut self,
1079        details: OnConsumeAddressReservation,
1080    ) -> ControlFlow<Self::Output> {
1081        ControlFlow::Continue(())
1082    }
1083
1084    #[must_use]
1085    fn on_new_named_address(&mut self, details: OnNewNamedAddress) -> ControlFlow<Self::Output> {
1086        ControlFlow::Continue(())
1087    }
1088
1089    #[must_use]
1090    fn on_new_intent(&mut self, details: OnNewIntent) -> ControlFlow<Self::Output> {
1091        ControlFlow::Continue(())
1092    }
1093
1094    #[must_use]
1095    fn on_drop_authzone_proofs(
1096        &mut self,
1097        details: OnDropAuthZoneProofs,
1098    ) -> ControlFlow<Self::Output> {
1099        ControlFlow::Continue(())
1100    }
1101
1102    #[must_use]
1103    fn on_pass_expression(&mut self, details: OnPassExpression) -> ControlFlow<Self::Output> {
1104        ControlFlow::Continue(())
1105    }
1106
1107    #[must_use]
1108    fn on_register_blob(&mut self, details: OnRegisterBlob) -> ControlFlow<Self::Output> {
1109        ControlFlow::Continue(())
1110    }
1111
1112    #[must_use]
1113    fn on_pass_blob(&mut self, details: OnPassBlob) -> ControlFlow<Self::Output> {
1114        ControlFlow::Continue(())
1115    }
1116
1117    #[must_use]
1118    fn on_resource_assertion(&mut self, details: OnResourceAssertion) -> ControlFlow<Self::Output> {
1119        ControlFlow::Continue(())
1120    }
1121
1122    #[must_use]
1123    fn on_verification(&mut self, details: OnVerification) -> ControlFlow<Self::Output> {
1124        ControlFlow::Continue(())
1125    }
1126
1127    #[must_use]
1128    fn on_finish(&mut self, details: OnFinish) -> ControlFlow<Self::Output> {
1129        ControlFlow::Continue(())
1130    }
1131}
1132
1133impl ManifestInterpretationVisitor for () {
1134    type Output = ManifestValidationError;
1135}
1136
1137/// The instruction has not yet passed validity checks, therefore is not included in the event.
1138pub struct OnStartInstruction {
1139    pub index: usize,
1140}
1141
1142/// The instruction and its effects have passed validity checks.
1143pub struct OnEndInstruction<'a> {
1144    pub index: usize,
1145    pub effect: ManifestInstructionEffect<'a>,
1146}
1147
1148/// A valid bucket has been created.
1149pub struct OnNewBucket<'s, 'a> {
1150    pub bucket: ManifestBucket,
1151    pub state: &'s BucketState<'a>,
1152}
1153
1154/// A bucket has been consumed in a valid manner.
1155pub struct OnConsumeBucket<'s, 'a> {
1156    pub bucket: ManifestBucket,
1157    pub state: &'s BucketState<'a>,
1158    pub destination: BucketDestination<'a>,
1159}
1160
1161/// A valid proof has been created.
1162pub struct OnNewProof<'s, 'a> {
1163    pub proof: ManifestProof,
1164    pub state: &'s ProofState<'a>,
1165}
1166
1167/// A proof has been consumed in a valid manner.
1168pub struct OnConsumeProof<'s, 'a> {
1169    pub proof: ManifestProof,
1170    pub state: &'s ProofState<'a>,
1171    pub destination: ProofDestination<'a>,
1172}
1173
1174/// A valid address reservation has been created.
1175pub struct OnNewAddressReservation<'s, 'a> {
1176    pub address_reservation: ManifestAddressReservation,
1177    pub state: &'s AddressReservationState<'a>,
1178}
1179
1180/// An address reservation has been consumed in a valid manner.
1181pub struct OnConsumeAddressReservation<'s, 'a> {
1182    pub address_reservation: ManifestAddressReservation,
1183    pub state: &'s AddressReservationState<'a>,
1184    pub destination: AddressReservationDestination<'a>,
1185}
1186
1187/// A valid named address has been created.
1188pub struct OnNewNamedAddress<'s, 'a> {
1189    pub named_address: ManifestNamedAddress,
1190    pub state: &'s NamedAddressState<'a>,
1191    pub package_address: &'a PackageAddress,
1192    pub blueprint_name: &'a str,
1193}
1194
1195/// A valid named intent has been created.
1196pub struct OnNewIntent<'s, 'a> {
1197    pub intent: ManifestNamedIntent,
1198    pub state: &'s IntentState<'a>,
1199}
1200
1201/// A valid auth zone bulk-drop instruction has been processed.
1202pub struct OnDropAuthZoneProofs {
1203    pub drop_all_signature_proofs: bool,
1204    pub drop_all_non_signature_proofs: bool,
1205}
1206
1207/// An expression has been passed into an invocation.
1208pub struct OnPassExpression<'a> {
1209    pub expression: ManifestExpression,
1210    pub destination: ExpressionDestination<'a>,
1211}
1212
1213/// A blob has been registered
1214pub struct OnRegisterBlob<'a> {
1215    pub blob_ref: ManifestBlobRef,
1216    pub content: &'a [u8],
1217}
1218
1219/// A blob has been passed into an invocation.
1220pub struct OnPassBlob<'a> {
1221    pub blob_ref: ManifestBlobRef,
1222    pub destination: BlobDestination<'a>,
1223}
1224
1225/// A valid assertion has been processed.
1226pub struct OnResourceAssertion<'a> {
1227    pub assertion: ResourceAssertion<'a>,
1228}
1229
1230/// A valid verification has been processed.
1231pub struct OnVerification<'a> {
1232    pub kind: VerificationKind,
1233    pub access_rule: &'a AccessRule,
1234}
1235
1236/// The manifest has finished processing, and has passed validity checks.
1237pub struct OnFinish;