radix_common/data/scrypto/
custom_schema.rs

1use crate::internal_prelude::*;
2
3pub type ScryptoTypeKind<L> = TypeKind<ScryptoCustomTypeKind, L>;
4pub type ScryptoLocalTypeKind = LocalTypeKind<ScryptoCustomSchema>;
5pub type ScryptoAggregatorTypeKind = AggregatorTypeKind<ScryptoCustomSchema>;
6pub type VersionedScryptoSchema = VersionedSchema<ScryptoCustomSchema>;
7pub type ScryptoSingleTypeSchema = SingleTypeSchema<ScryptoCustomSchema>;
8pub type ScryptoTypeCollectionSchema = TypeCollectionSchema<ScryptoCustomSchema>;
9pub type ScryptoSchema = Schema<ScryptoCustomSchema>;
10pub type ScryptoTypeData<L> = TypeData<ScryptoCustomTypeKind, L>;
11pub type ScryptoLocalTypeData = LocalTypeData<ScryptoCustomSchema>;
12pub type ScryptoAggregatorTypeData = AggregatorTypeData<ScryptoCustomSchema>;
13pub type ScryptoTypeValidation = TypeValidation<ScryptoCustomTypeValidation>;
14pub type ScryptoTypeAggregator = TypeAggregator<ScryptoCustomTypeKind>;
15
16pub trait ScryptoCheckedFixedSchema: CheckedFixedSchema<ScryptoCustomSchema> {}
17impl<T: CheckedFixedSchema<ScryptoCustomSchema>> ScryptoCheckedFixedSchema for T {}
18
19pub trait ScryptoCheckedBackwardsCompatibleSchema:
20    CheckedBackwardsCompatibleSchema<ScryptoCustomSchema>
21{
22}
23impl<T: CheckedBackwardsCompatibleSchema<ScryptoCustomSchema>>
24    ScryptoCheckedBackwardsCompatibleSchema for T
25{
26}
27
28/// A schema for the values that a codec can decode / views as valid
29#[derive(Debug, Clone, PartialEq, Eq, ManifestSbor, ScryptoSbor)]
30pub enum ScryptoCustomTypeKind {
31    Reference,
32    Own,
33    Decimal,
34    PreciseDecimal,
35    NonFungibleLocalId,
36}
37
38#[derive(Debug, Copy, Clone, PartialEq, Eq, ManifestSbor, ScryptoSbor)]
39pub enum ScryptoCustomTypeKindLabel {
40    Reference,
41    Own,
42    Decimal,
43    PreciseDecimal,
44    NonFungibleLocalId,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, ManifestSbor, ScryptoSbor)]
48pub enum ScryptoCustomTypeValidation {
49    Reference(ReferenceValidation),
50    Own(OwnValidation),
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, ManifestSbor, ScryptoSbor)]
54pub enum ReferenceValidation {
55    IsGlobal,
56    IsGlobalPackage,
57    IsGlobalComponent,
58    IsGlobalResourceManager,
59    IsGlobalTyped(Option<PackageAddress>, String),
60    IsInternal,
61    IsInternalTyped(Option<PackageAddress>, String),
62}
63
64impl ReferenceValidation {
65    fn compare(base: &Self, compared: &Self) -> ValidationChange {
66        match (base, compared) {
67            (base, compared) if base == compared => ValidationChange::Unchanged,
68            (ReferenceValidation::IsGlobal, compared) if compared.requires_global() => {
69                ValidationChange::Strengthened
70            }
71            (base, ReferenceValidation::IsGlobal) if base.requires_global() => {
72                ValidationChange::Weakened
73            }
74            (ReferenceValidation::IsInternal, compared) if compared.requires_internal() => {
75                ValidationChange::Strengthened
76            }
77            (base, ReferenceValidation::IsInternal) if base.requires_internal() => {
78                ValidationChange::Weakened
79            }
80            (_, _) => ValidationChange::Incomparable,
81        }
82    }
83
84    fn requires_global(&self) -> bool {
85        match self {
86            ReferenceValidation::IsGlobal => true,
87            ReferenceValidation::IsGlobalPackage => true,
88            ReferenceValidation::IsGlobalComponent => true,
89            ReferenceValidation::IsGlobalResourceManager => true,
90            ReferenceValidation::IsGlobalTyped(_, _) => true,
91            ReferenceValidation::IsInternal => false,
92            ReferenceValidation::IsInternalTyped(_, _) => false,
93        }
94    }
95
96    fn requires_internal(&self) -> bool {
97        match self {
98            ReferenceValidation::IsGlobal => false,
99            ReferenceValidation::IsGlobalPackage => false,
100            ReferenceValidation::IsGlobalComponent => false,
101            ReferenceValidation::IsGlobalResourceManager => false,
102            ReferenceValidation::IsGlobalTyped(_, _) => false,
103            ReferenceValidation::IsInternal => true,
104            ReferenceValidation::IsInternalTyped(_, _) => true,
105        }
106    }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, ManifestSbor, ScryptoSbor)]
110pub enum OwnValidation {
111    IsBucket,
112    IsProof,
113    IsVault,
114    IsKeyValueStore,
115    IsGlobalAddressReservation,
116    IsTypedObject(Option<PackageAddress>, String),
117}
118
119impl OwnValidation {
120    fn compare(base: &Self, compared: &Self) -> ValidationChange {
121        // This is strictly a little hard - if we get issues, we may wish to match
122        // IsTypedObject(ResourcePackage, "FungibleBucket") as a strengthening of IsBucket and so on.
123        if base == compared {
124            ValidationChange::Unchanged
125        } else {
126            ValidationChange::Incomparable
127        }
128    }
129
130    pub fn could_match_manifest_bucket(&self) -> bool {
131        match self {
132            OwnValidation::IsBucket => true,
133            OwnValidation::IsProof => false,
134            OwnValidation::IsVault => false,
135            OwnValidation::IsKeyValueStore => false,
136            OwnValidation::IsGlobalAddressReservation => false,
137            // Hard to validate without knowing package addresses from engine, assume fine
138            OwnValidation::IsTypedObject(_, _) => true,
139        }
140    }
141
142    pub fn could_match_manifest_proof(&self) -> bool {
143        match self {
144            OwnValidation::IsBucket => false,
145            OwnValidation::IsProof => true,
146            OwnValidation::IsVault => false,
147            OwnValidation::IsKeyValueStore => false,
148            OwnValidation::IsGlobalAddressReservation => false,
149            // Hard to validate without knowing package addresses from engine, assume fine
150            OwnValidation::IsTypedObject(_, _) => true,
151        }
152    }
153
154    pub fn could_match_manifest_address_reservation(&self) -> bool {
155        match self {
156            OwnValidation::IsBucket => false,
157            OwnValidation::IsProof => false,
158            OwnValidation::IsVault => false,
159            OwnValidation::IsKeyValueStore => false,
160            OwnValidation::IsGlobalAddressReservation => true,
161            OwnValidation::IsTypedObject(_, _) => false,
162        }
163    }
164}
165
166impl ReferenceValidation {
167    pub fn could_match_manifest_address(&self) -> bool {
168        match self {
169            ReferenceValidation::IsGlobal => true,
170            ReferenceValidation::IsGlobalPackage => true,
171            ReferenceValidation::IsGlobalComponent => true,
172            ReferenceValidation::IsGlobalResourceManager => true,
173            ReferenceValidation::IsGlobalTyped(_, _) => true,
174            ReferenceValidation::IsInternal => true,
175            ReferenceValidation::IsInternalTyped(_, _) => true,
176        }
177    }
178}
179
180impl<L: SchemaTypeLink> CustomTypeKind<L> for ScryptoCustomTypeKind {
181    type CustomTypeValidation = ScryptoCustomTypeValidation;
182    type CustomTypeKindLabel = ScryptoCustomTypeKindLabel;
183
184    fn label(&self) -> Self::CustomTypeKindLabel {
185        match self {
186            ScryptoCustomTypeKind::Reference => ScryptoCustomTypeKindLabel::Reference,
187            ScryptoCustomTypeKind::Own => ScryptoCustomTypeKindLabel::Own,
188            ScryptoCustomTypeKind::Decimal => ScryptoCustomTypeKindLabel::Decimal,
189            ScryptoCustomTypeKind::PreciseDecimal => ScryptoCustomTypeKindLabel::PreciseDecimal,
190            ScryptoCustomTypeKind::NonFungibleLocalId => {
191                ScryptoCustomTypeKindLabel::NonFungibleLocalId
192            }
193        }
194    }
195}
196
197impl CustomTypeKindLabel for ScryptoCustomTypeKindLabel {
198    fn name(&self) -> &'static str {
199        match self {
200            ScryptoCustomTypeKindLabel::Reference => "Reference",
201            ScryptoCustomTypeKindLabel::Own => "Own",
202            ScryptoCustomTypeKindLabel::Decimal => "Decimal",
203            ScryptoCustomTypeKindLabel::PreciseDecimal => "PreciseDecimal",
204            ScryptoCustomTypeKindLabel::NonFungibleLocalId => "NonFungibleLocalId",
205        }
206    }
207}
208
209impl CustomTypeValidation for ScryptoCustomTypeValidation {
210    fn compare(base: &Self, compared: &Self) -> ValidationChange {
211        match (base, compared) {
212            (
213                ScryptoCustomTypeValidation::Reference(base),
214                ScryptoCustomTypeValidation::Reference(compared),
215            ) => ReferenceValidation::compare(base, compared),
216            (ScryptoCustomTypeValidation::Reference(_), ScryptoCustomTypeValidation::Own(_)) => {
217                ValidationChange::Incomparable
218            }
219            (ScryptoCustomTypeValidation::Own(_), ScryptoCustomTypeValidation::Reference(_)) => {
220                ValidationChange::Incomparable
221            }
222            (
223                ScryptoCustomTypeValidation::Own(base),
224                ScryptoCustomTypeValidation::Own(compared),
225            ) => OwnValidation::compare(base, compared),
226        }
227    }
228}
229
230#[derive(Debug, Clone, PartialEq, Eq, Copy)]
231pub struct ScryptoCustomSchema {}
232
233lazy_static::lazy_static! {
234    static ref EMPTY_SCHEMA: Schema<ScryptoCustomSchema> = {
235        Schema::empty()
236    };
237}
238
239impl CustomSchema for ScryptoCustomSchema {
240    type CustomLocalTypeKind = ScryptoCustomTypeKind;
241    type CustomAggregatorTypeKind = ScryptoCustomTypeKind;
242    type CustomTypeKindLabel = ScryptoCustomTypeKindLabel;
243    type CustomTypeValidation = ScryptoCustomTypeValidation;
244    type DefaultCustomExtension = ScryptoCustomExtension;
245
246    fn linearize_type_kind(
247        type_kind: Self::CustomLocalTypeKind,
248        _type_indices: &IndexSet<TypeHash>,
249    ) -> Self::CustomAggregatorTypeKind {
250        type_kind
251    }
252
253    fn resolve_well_known_type(
254        well_known_id: WellKnownTypeId,
255    ) -> Option<&'static LocalTypeData<Self>> {
256        resolve_scrypto_well_known_type(well_known_id)
257    }
258
259    fn validate_custom_type_kind(
260        _context: &SchemaContext,
261        type_kind: &Self::CustomLocalTypeKind,
262    ) -> Result<(), SchemaValidationError> {
263        match type_kind {
264            ScryptoCustomTypeKind::Reference
265            | ScryptoCustomTypeKind::Own
266            | ScryptoCustomTypeKind::Decimal
267            | ScryptoCustomTypeKind::PreciseDecimal
268            | ScryptoCustomTypeKind::NonFungibleLocalId => {
269                // No validations
270            }
271        }
272        Ok(())
273    }
274
275    fn validate_type_metadata_with_custom_type_kind(
276        _: &SchemaContext,
277        type_kind: &Self::CustomLocalTypeKind,
278        type_metadata: &TypeMetadata,
279    ) -> Result<(), SchemaValidationError> {
280        // Even though they all map to the same thing, we keep the explicit match statement so that
281        // we will have to explicitly check this when we add a new `ScryptoCustomTypeKind`
282        match type_kind {
283            ScryptoCustomTypeKind::Reference
284            | ScryptoCustomTypeKind::Own
285            | ScryptoCustomTypeKind::Decimal
286            | ScryptoCustomTypeKind::PreciseDecimal
287            | ScryptoCustomTypeKind::NonFungibleLocalId => {
288                validate_childless_metadata(type_metadata)?;
289            }
290        }
291        Ok(())
292    }
293
294    fn validate_custom_type_validation(
295        _context: &SchemaContext,
296        custom_type_kind: &Self::CustomLocalTypeKind,
297        custom_type_validation: &Self::CustomTypeValidation,
298    ) -> Result<(), SchemaValidationError> {
299        match custom_type_kind {
300            ScryptoCustomTypeKind::Reference => {
301                if let ScryptoCustomTypeValidation::Reference(_) = custom_type_validation {
302                    Ok(())
303                } else {
304                    Err(SchemaValidationError::TypeValidationMismatch)
305                }
306            }
307            ScryptoCustomTypeKind::Own => {
308                if let ScryptoCustomTypeValidation::Own(_) = custom_type_validation {
309                    Ok(())
310                } else {
311                    Err(SchemaValidationError::TypeValidationMismatch)
312                }
313            }
314            ScryptoCustomTypeKind::Decimal
315            | ScryptoCustomTypeKind::PreciseDecimal
316            | ScryptoCustomTypeKind::NonFungibleLocalId => {
317                // All these custom type kinds only support `SchemaTypeValidation::None`.
318                // If they get to this point, they have been paired with some ScryptoCustomTypeValidation
319                // - which isn't valid.
320                Err(SchemaValidationError::TypeValidationMismatch)
321            }
322        }
323    }
324
325    fn empty_schema() -> &'static Schema<Self> {
326        &EMPTY_SCHEMA
327    }
328}
329
330pub trait HasSchemaHash {
331    fn generate_schema_hash(&self) -> SchemaHash;
332}
333
334impl HasSchemaHash for VersionedScryptoSchema {
335    fn generate_schema_hash(&self) -> SchemaHash {
336        SchemaHash::from(hash(scrypto_encode(self).unwrap()))
337    }
338}
339
340pub fn replace_self_package_address(
341    schema: &mut VersionedScryptoSchema,
342    package_address: PackageAddress,
343) {
344    for type_validation in &mut schema.v1_mut().type_validations {
345        match type_validation {
346            TypeValidation::Custom(ScryptoCustomTypeValidation::Own(
347                OwnValidation::IsTypedObject(package, _),
348            ))
349            | TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
350                ReferenceValidation::IsGlobalTyped(package, _),
351            ))
352            | TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
353                ReferenceValidation::IsInternalTyped(package, _),
354            )) => {
355                if package.is_none() {
356                    *package = Some(package_address)
357                }
358            }
359            _ => {}
360        }
361    }
362}