radix_common/data/scrypto/
custom_validation.rs

1use crate::internal_prelude::*;
2
3//=======================================================================================================
4// NOTE:
5// The validation implemented here is static and can be used where we can't create a TypeInfo lookup.
6//
7// There is more powerful validation defined in `payload_validation.rs` in the `radix-engine` crate
8// which does requires creating the lookup, and checks package/blueprint names of objects.
9//=======================================================================================================
10
11impl<'a> ValidatableCustomExtension<()> for ScryptoCustomExtension {
12    fn apply_validation_for_custom_value<'de>(
13        schema: &Schema<Self::CustomSchema>,
14        custom_value: &<Self::CustomTraversal as traversal::CustomTraversal>::CustomTerminalValueRef<'de>,
15        type_id: LocalTypeId,
16        _: &(),
17    ) -> Result<(), PayloadValidationError<Self>> {
18        match schema
19            .resolve_type_validation(type_id)
20            .ok_or(PayloadValidationError::SchemaInconsistency)?
21        {
22            TypeValidation::None => Ok(()),
23            TypeValidation::Custom(custom_validation) => {
24                apply_static_custom_validation_to_custom_value(custom_validation, &custom_value.0)
25            }
26            _ => Err(PayloadValidationError::SchemaInconsistency),
27        }
28    }
29
30    fn apply_custom_type_validation_for_non_custom_value<'de>(
31        _: &Schema<Self::CustomSchema>,
32        _: &<Self::CustomSchema as CustomSchema>::CustomTypeValidation,
33        _: &TerminalValueRef<'de, Self::CustomTraversal>,
34        _: &(),
35    ) -> Result<(), PayloadValidationError<Self>> {
36        // Non-custom values must have non-custom type kinds...
37        // But custom type validations aren't allowed to combine with non-custom type kinds
38        Err(PayloadValidationError::SchemaInconsistency)
39    }
40}
41
42fn apply_static_custom_validation_to_custom_value(
43    custom_validation: &ScryptoCustomTypeValidation,
44    custom_value: &ScryptoCustomValue,
45) -> Result<(), PayloadValidationError<ScryptoCustomExtension>> {
46    match custom_validation {
47        ScryptoCustomTypeValidation::Reference(reference_validation) => {
48            let ScryptoCustomValue::Reference(reference) = custom_value else {
49                return Err(PayloadValidationError::SchemaInconsistency);
50            };
51            let node_id = reference.0;
52            let is_valid = match &reference_validation {
53                ReferenceValidation::IsGlobal => node_id.is_global(),
54                ReferenceValidation::IsGlobalPackage => node_id.is_global_package(),
55                ReferenceValidation::IsGlobalComponent => node_id.is_global_component(),
56                ReferenceValidation::IsGlobalResourceManager => {
57                    node_id.is_global_resource_manager()
58                }
59                // We can't check this statically without a type_info lookup, so assume valid
60                ReferenceValidation::IsGlobalTyped(_, _) => node_id.is_global(),
61                ReferenceValidation::IsInternal => node_id.is_internal(),
62                // We can't check this statically without a type_info lookup, so assume valid
63                ReferenceValidation::IsInternalTyped(_, _) => node_id.is_internal(),
64            };
65            if !is_valid {
66                return Err(PayloadValidationError::ValidationError(
67                    ValidationError::CustomError(format!(
68                        "Expected = Reference<{:?}>, found node id with entity type: {:?}",
69                        reference_validation,
70                        node_id.entity_type()
71                    )),
72                ));
73            }
74        }
75        ScryptoCustomTypeValidation::Own(own_validation) => {
76            let ScryptoCustomValue::Own(own) = custom_value else {
77                return Err(PayloadValidationError::SchemaInconsistency);
78            };
79            let node_id = own.0;
80            // We can't check the type_info details statically, so we do the best we can with the entity byte
81            let is_valid = match own_validation {
82                OwnValidation::IsBucket => node_id.is_internal(),
83                OwnValidation::IsProof => node_id.is_internal(),
84                OwnValidation::IsVault => node_id.is_internal_vault(),
85                OwnValidation::IsKeyValueStore => node_id.is_internal_kv_store(),
86                OwnValidation::IsGlobalAddressReservation => true,
87                OwnValidation::IsTypedObject(_, _) => true,
88            };
89            if !is_valid {
90                return Err(PayloadValidationError::ValidationError(
91                    ValidationError::CustomError(format!(
92                        "Expected = Own<{:?}>, found node id with entity type: {:?}",
93                        own_validation,
94                        node_id.entity_type()
95                    )),
96                ));
97            }
98        }
99    };
100    Ok(())
101}