Skip to content

Commit

Permalink
Merge pull request #1836 from radixdlt/feature/schema-comparisons
Browse files Browse the repository at this point in the history
Feature/schema comparisons
  • Loading branch information
dhedey authored Aug 7, 2024
2 parents 184da10 + 0b009e8 commit be92477
Show file tree
Hide file tree
Showing 67 changed files with 5,365 additions and 799 deletions.
1 change: 1 addition & 0 deletions radix-clis/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion radix-clis/src/scrypto_bindgen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ where
fn resolve_type_kind(
&self,
type_identifier: &ScopedTypeId,
) -> Result<SchemaTypeKind<ScryptoCustomSchema>, schema::SchemaError> {
) -> Result<LocalTypeKind<ScryptoCustomSchema>, schema::SchemaError> {
self.lookup_schema(&type_identifier.0)
.ok_or(schema::SchemaError::FailedToGetSchemaFromSchemaHash)?
.as_latest_version()
Expand Down
7 changes: 2 additions & 5 deletions radix-common/src/data/manifest/custom_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ impl CustomExtension for ManifestCustomExtension {
fn custom_value_kind_matches_type_kind(
schema: &Schema<Self::CustomSchema>,
custom_value_kind: Self::CustomValueKind,
type_kind: &TypeKind<
<Self::CustomSchema as CustomSchema>::CustomTypeKind<LocalTypeId>,
LocalTypeId,
>,
type_kind: &LocalTypeKind<Self::CustomSchema>,
) -> bool {
match custom_value_kind {
ManifestCustomValueKind::Address => matches!(
Expand Down Expand Up @@ -67,7 +64,7 @@ impl CustomExtension for ManifestCustomExtension {

fn custom_type_kind_matches_non_custom_value_kind(
_: &Schema<Self::CustomSchema>,
_: &<Self::CustomSchema as CustomSchema>::CustomTypeKind<LocalTypeId>,
_: &<Self::CustomSchema as CustomSchema>::CustomLocalTypeKind,
_: ValueKind<Self::CustomValueKind>,
) -> bool {
// No custom type kinds can match non-custom value kinds
Expand Down
2 changes: 1 addition & 1 deletion radix-common/src/data/manifest/custom_traversal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl CustomTraversal for ManifestCustomTraversal {
type CustomValueKind = ManifestCustomValueKind;
type CustomTerminalValueRef<'de> = ManifestCustomTerminalValueRef;

fn decode_custom_value_body<'de, R>(
fn read_custom_value_body<'de, R>(
custom_value_kind: Self::CustomValueKind,
reader: &mut R,
) -> Result<Self::CustomTerminalValueRef<'de>, DecodeError>
Expand Down
31 changes: 31 additions & 0 deletions radix-common/src/data/manifest/definitions.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use vec_traits::vec_decode_with_nice_error;

use crate::internal_prelude::*;

pub use crate::constants::MANIFEST_SBOR_V1_MAX_DEPTH;
Expand Down Expand Up @@ -110,6 +112,35 @@ pub fn manifest_decode_with_depth_limit<T: ManifestDecode>(
ManifestDecoder::new(buf, depth_limit).decode_payload(MANIFEST_SBOR_V1_PAYLOAD_PREFIX)
}

/// Decodes a data structure from a byte array.
///
/// If an error occurs, the type's schema is exported and used to give a better error message.
///
/// NOTE:
/// * The error path runs very slowly. This should only be used where errors are NOT expected.
/// * This should not be used where the size of compiled code is an issue, as it will pull
/// in the schema aggregation code which is large.
pub fn manifest_decode_with_nice_error<T: ManifestDecode + ScryptoDescribe>(
buf: &[u8],
) -> Result<T, String> {
vec_decode_with_nice_error::<ManifestCustomExtension, T>(buf, MANIFEST_SBOR_V1_MAX_DEPTH)
}

/// Decodes a data structure from a byte array.
///
/// If an error occurs, the type's schema is exported and used to give a better error message.
///
/// NOTE:
/// * The error path runs very slowly. This should only be used where errors are NOT expected.
/// * This should not be used where the size of compiled code is an issue, as it will pull
/// in the schema aggregation code which is large.
pub fn manifest_decode_with_depth_limit_and_nice_error<T: ManifestDecode + ScryptoDescribe>(
buf: &[u8],
depth_limit: usize,
) -> Result<T, String> {
vec_decode_with_nice_error::<ManifestCustomExtension, T>(buf, depth_limit)
}

pub fn to_manifest_value<T: ManifestEncode + ?Sized>(
value: &T,
) -> Result<ManifestValue, RustToManifestValueError> {
Expand Down
7 changes: 2 additions & 5 deletions radix-common/src/data/scrypto/custom_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ impl CustomExtension for ScryptoCustomExtension {
fn custom_value_kind_matches_type_kind(
_: &Schema<Self::CustomSchema>,
custom_value_kind: Self::CustomValueKind,
type_kind: &TypeKind<
<Self::CustomSchema as CustomSchema>::CustomTypeKind<LocalTypeId>,
LocalTypeId,
>,
type_kind: &LocalTypeKind<Self::CustomSchema>,
) -> bool {
match custom_value_kind {
ScryptoCustomValueKind::Reference => matches!(
Expand All @@ -42,7 +39,7 @@ impl CustomExtension for ScryptoCustomExtension {

fn custom_type_kind_matches_non_custom_value_kind(
_: &Schema<Self::CustomSchema>,
_: &<Self::CustomSchema as CustomSchema>::CustomTypeKind<LocalTypeId>,
_: &<Self::CustomSchema as CustomSchema>::CustomLocalTypeKind,
_: ValueKind<Self::CustomValueKind>,
) -> bool {
// It's not possible for a custom type kind to match a non-custom value kind
Expand Down
141 changes: 126 additions & 15 deletions radix-common/src/data/scrypto/custom_schema.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
use crate::internal_prelude::*;

pub type ScryptoTypeKind<L> = TypeKind<ScryptoCustomTypeKind, L>;
pub type ScryptoLocalTypeKind = LocalTypeKind<ScryptoCustomSchema>;
pub type ScryptoAggregatorTypeKind = AggregatorTypeKind<ScryptoCustomSchema>;
pub type VersionedScryptoSchema = VersionedSchema<ScryptoCustomSchema>;
pub type ScryptoSchema = Schema<ScryptoCustomSchema>;
pub type ScryptoTypeData<L> = TypeData<ScryptoCustomTypeKind, L>;
pub type ScryptoLocalTypeData = LocalTypeData<ScryptoCustomSchema>;
pub type ScryptoAggregatorTypeData = AggregatorTypeData<ScryptoCustomSchema>;
pub type ScryptoTypeValidation = TypeValidation<ScryptoCustomTypeValidation>;
pub type ScryptoTypeAggregator = TypeAggregator<ScryptoCustomTypeKind>;

/// A schema for the values that a codec can decode / views as valid
#[derive(Debug, Clone, PartialEq, Eq, ManifestSbor, ScryptoSbor)]
Expand All @@ -15,6 +21,15 @@ pub enum ScryptoCustomTypeKind {
NonFungibleLocalId,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, ManifestSbor, ScryptoSbor)]
pub enum ScryptoCustomTypeKindLabel {
Reference,
Own,
Decimal,
PreciseDecimal,
NonFungibleLocalId,
}

#[derive(Debug, Clone, PartialEq, Eq, ManifestSbor, ScryptoSbor)]
pub enum ScryptoCustomTypeValidation {
Reference(ReferenceValidation),
Expand All @@ -32,6 +47,51 @@ pub enum ReferenceValidation {
IsInternalTyped(Option<PackageAddress>, String),
}

impl ReferenceValidation {
fn compare(base: &Self, compared: &Self) -> ValidationChange {
match (base, compared) {
(base, compared) if base == compared => ValidationChange::Unchanged,
(ReferenceValidation::IsGlobal, compared) if compared.requires_global() => {
ValidationChange::Strengthened
}
(base, ReferenceValidation::IsGlobal) if base.requires_global() => {
ValidationChange::Weakened
}
(ReferenceValidation::IsInternal, compared) if compared.requires_internal() => {
ValidationChange::Strengthened
}
(base, ReferenceValidation::IsInternal) if base.requires_internal() => {
ValidationChange::Weakened
}
(_, _) => ValidationChange::Incomparable,
}
}

fn requires_global(&self) -> bool {
match self {
ReferenceValidation::IsGlobal => true,
ReferenceValidation::IsGlobalPackage => true,
ReferenceValidation::IsGlobalComponent => true,
ReferenceValidation::IsGlobalResourceManager => true,
ReferenceValidation::IsGlobalTyped(_, _) => true,
ReferenceValidation::IsInternal => false,
ReferenceValidation::IsInternalTyped(_, _) => false,
}
}

fn requires_internal(&self) -> bool {
match self {
ReferenceValidation::IsGlobal => false,
ReferenceValidation::IsGlobalPackage => false,
ReferenceValidation::IsGlobalComponent => false,
ReferenceValidation::IsGlobalResourceManager => false,
ReferenceValidation::IsGlobalTyped(_, _) => false,
ReferenceValidation::IsInternal => true,
ReferenceValidation::IsInternalTyped(_, _) => true,
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, ManifestSbor, ScryptoSbor)]
pub enum OwnValidation {
IsBucket,
Expand All @@ -43,6 +103,16 @@ pub enum OwnValidation {
}

impl OwnValidation {
fn compare(base: &Self, compared: &Self) -> ValidationChange {
// This is strictly a little hard - if we get issues, we may wish to match
// IsTypedObject(ResourcePackage, "FungibleBucket") as a strengthening of IsBucket and so on.
if base == compared {
ValidationChange::Unchanged
} else {
ValidationChange::Incomparable
}
}

pub fn could_match_manifest_bucket(&self) -> bool {
match self {
OwnValidation::IsBucket => true,
Expand Down Expand Up @@ -95,9 +165,53 @@ impl ReferenceValidation {

impl<L: SchemaTypeLink> CustomTypeKind<L> for ScryptoCustomTypeKind {
type CustomTypeValidation = ScryptoCustomTypeValidation;
type CustomTypeKindLabel = ScryptoCustomTypeKindLabel;

fn label(&self) -> Self::CustomTypeKindLabel {
match self {
ScryptoCustomTypeKind::Reference => ScryptoCustomTypeKindLabel::Reference,
ScryptoCustomTypeKind::Own => ScryptoCustomTypeKindLabel::Own,
ScryptoCustomTypeKind::Decimal => ScryptoCustomTypeKindLabel::Decimal,
ScryptoCustomTypeKind::PreciseDecimal => ScryptoCustomTypeKindLabel::PreciseDecimal,
ScryptoCustomTypeKind::NonFungibleLocalId => {
ScryptoCustomTypeKindLabel::NonFungibleLocalId
}
}
}
}

impl CustomTypeValidation for ScryptoCustomTypeValidation {}
impl CustomTypeKindLabel for ScryptoCustomTypeKindLabel {
fn name(&self) -> &'static str {
match self {
ScryptoCustomTypeKindLabel::Reference => "Reference",
ScryptoCustomTypeKindLabel::Own => "Own",
ScryptoCustomTypeKindLabel::Decimal => "Decimal",
ScryptoCustomTypeKindLabel::PreciseDecimal => "PreciseDecimal",
ScryptoCustomTypeKindLabel::NonFungibleLocalId => "NonFungibleLocalId",
}
}
}

impl CustomTypeValidation for ScryptoCustomTypeValidation {
fn compare(base: &Self, compared: &Self) -> ValidationChange {
match (base, compared) {
(
ScryptoCustomTypeValidation::Reference(base),
ScryptoCustomTypeValidation::Reference(compared),
) => ReferenceValidation::compare(base, compared),
(ScryptoCustomTypeValidation::Reference(_), ScryptoCustomTypeValidation::Own(_)) => {
ValidationChange::Incomparable
}
(ScryptoCustomTypeValidation::Own(_), ScryptoCustomTypeValidation::Reference(_)) => {
ValidationChange::Incomparable
}
(
ScryptoCustomTypeValidation::Own(base),
ScryptoCustomTypeValidation::Own(compared),
) => OwnValidation::compare(base, compared),
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct ScryptoCustomSchema {}
Expand All @@ -109,31 +223,28 @@ lazy_static::lazy_static! {
}

impl CustomSchema for ScryptoCustomSchema {
type CustomTypeKind<L: SchemaTypeLink> = ScryptoCustomTypeKind;
type CustomLocalTypeKind = ScryptoCustomTypeKind;
type CustomAggregatorTypeKind = ScryptoCustomTypeKind;
type CustomTypeKindLabel = ScryptoCustomTypeKindLabel;
type CustomTypeValidation = ScryptoCustomTypeValidation;
type DefaultCustomExtension = ScryptoCustomExtension;

fn linearize_type_kind(
type_kind: Self::CustomTypeKind<RustTypeId>,
type_kind: Self::CustomLocalTypeKind,
_type_indices: &IndexSet<TypeHash>,
) -> Self::CustomTypeKind<LocalTypeId> {
match type_kind {
ScryptoCustomTypeKind::Reference => ScryptoCustomTypeKind::Reference,
ScryptoCustomTypeKind::Own => ScryptoCustomTypeKind::Own,
ScryptoCustomTypeKind::Decimal => ScryptoCustomTypeKind::Decimal,
ScryptoCustomTypeKind::PreciseDecimal => ScryptoCustomTypeKind::PreciseDecimal,
ScryptoCustomTypeKind::NonFungibleLocalId => ScryptoCustomTypeKind::NonFungibleLocalId,
}
) -> Self::CustomAggregatorTypeKind {
type_kind
}

fn resolve_well_known_type(
well_known_id: WellKnownTypeId,
) -> Option<&'static TypeData<Self::CustomTypeKind<LocalTypeId>, LocalTypeId>> {
) -> Option<&'static LocalTypeData<Self>> {
resolve_scrypto_well_known_type(well_known_id)
}

fn validate_custom_type_kind(
_context: &SchemaContext,
type_kind: &Self::CustomTypeKind<LocalTypeId>,
type_kind: &Self::CustomLocalTypeKind,
) -> Result<(), SchemaValidationError> {
match type_kind {
ScryptoCustomTypeKind::Reference
Expand All @@ -149,7 +260,7 @@ impl CustomSchema for ScryptoCustomSchema {

fn validate_type_metadata_with_custom_type_kind(
_: &SchemaContext,
type_kind: &Self::CustomTypeKind<LocalTypeId>,
type_kind: &Self::CustomLocalTypeKind,
type_metadata: &TypeMetadata,
) -> Result<(), SchemaValidationError> {
// Even though they all map to the same thing, we keep the explicit match statement so that
Expand All @@ -168,7 +279,7 @@ impl CustomSchema for ScryptoCustomSchema {

fn validate_custom_type_validation(
_context: &SchemaContext,
custom_type_kind: &Self::CustomTypeKind<LocalTypeId>,
custom_type_kind: &Self::CustomLocalTypeKind,
custom_type_validation: &Self::CustomTypeValidation,
) -> Result<(), SchemaValidationError> {
match custom_type_kind {
Expand Down
2 changes: 1 addition & 1 deletion radix-common/src/data/scrypto/custom_traversal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl CustomTraversal for ScryptoCustomTraversal {
type CustomValueKind = ScryptoCustomValueKind;
type CustomTerminalValueRef<'de> = ScryptoCustomTerminalValueRef;

fn decode_custom_value_body<'de, R>(
fn read_custom_value_body<'de, R>(
custom_value_kind: Self::CustomValueKind,
reader: &mut R,
) -> Result<Self::CustomTerminalValueRef<'de>, DecodeError>
Expand Down
2 changes: 1 addition & 1 deletion radix-common/src/data/scrypto/custom_well_known_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ create_well_known_lookup!(

pub fn resolve_scrypto_well_known_type(
well_known_index: WellKnownTypeId,
) -> Option<&'static TypeData<ScryptoCustomTypeKind, LocalTypeId>> {
) -> Option<&'static ScryptoLocalTypeData> {
WELL_KNOWN_LOOKUP
.get(well_known_index.as_index())
.and_then(|x| x.as_ref())
Expand Down
29 changes: 29 additions & 0 deletions radix-common/src/data/scrypto/definitions.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use vec_traits::vec_decode_with_nice_error;

use crate::internal_prelude::*;

pub use crate::constants::SCRYPTO_SBOR_V1_MAX_DEPTH;
Expand Down Expand Up @@ -69,3 +71,30 @@ pub fn scrypto_decode_with_depth_limit<T: ScryptoDecode>(
) -> Result<T, DecodeError> {
ScryptoDecoder::new(buf, depth_limit).decode_payload(SCRYPTO_SBOR_V1_PAYLOAD_PREFIX)
}

/// Decodes a data structure from a byte array.
///
/// If an error occurs, the type's schema is exported and used to give a better error message.
///
/// NOTE:
/// * The error path runs very slowly. This should only be used where errors are NOT expected.
/// * This should not be used in Scrypto, as it will pull in the schema aggregation code which is large.
pub fn scrypto_decode_with_nice_error<T: ScryptoDecode + ScryptoDescribe>(
buf: &[u8],
) -> Result<T, String> {
vec_decode_with_nice_error::<ScryptoCustomExtension, T>(buf, SCRYPTO_SBOR_V1_MAX_DEPTH)
}

/// Decodes a data structure from a byte array.
///
/// If an error occurs, the type's schema is exported and used to give a better error message.
///
/// NOTE:
/// * The error path runs very slowly. This should only be used where errors are NOT expected.
/// * This should not be used in Scrypto, as it will pull in the schema aggregation code which is large.
pub fn scrypto_decode_with_depth_limit_and_nice_error<T: ScryptoDecode + ScryptoDescribe>(
buf: &[u8],
depth_limit: usize,
) -> Result<T, String> {
vec_decode_with_nice_error::<ScryptoCustomExtension, T>(buf, depth_limit)
}
4 changes: 2 additions & 2 deletions radix-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub use sbor::{Categorize, Decode, Encode, Sbor};
extern crate radix_sbor_derive;
pub use radix_sbor_derive::{
ManifestCategorize, ManifestDecode, ManifestEncode, ManifestSbor, ScryptoCategorize,
ScryptoDecode, ScryptoEncode, ScryptoEvent, ScryptoSbor,
ScryptoDecode, ScryptoEncode, ScryptoEvent, ScryptoSbor, ScryptoSborAssertion,
};

// extern crate self as X; in lib.rs allows ::X and X to resolve to this crate inside this crate.
Expand All @@ -60,7 +60,7 @@ pub mod prelude {
// Exports from upstream libraries
pub use radix_sbor_derive::{
ManifestCategorize, ManifestDecode, ManifestEncode, ManifestSbor, ScryptoCategorize,
ScryptoDecode, ScryptoEncode, ScryptoEvent, ScryptoSbor,
ScryptoDecode, ScryptoEncode, ScryptoEvent, ScryptoSbor, ScryptoSborAssertion,
};
pub use sbor::prelude::*;
pub use sbor::*;
Expand Down
Loading

0 comments on commit be92477

Please sign in to comment.