ScryptoSborAssertion

Derive Macro ScryptoSborAssertion 

Source
#[derive(ScryptoSborAssertion)]
{
    // Attributes available to this derive:
    #[sbor_assert]
}
Expand description

A macro for outputting tests and marker traits to assert that a type has maintained its shape over time.

There are two types of assertion modes:

  • fixed mode is used to ensure that a type is unchanged.
  • backwards_compatible mode is used when multiple versions of the type are permissible, but newer versions of the type must be compatible with the older version where they align. This mode (A) ensures that the type’s current schema is equivalent to the latest version, and (B) ensures that each of the schemas is a strict extension of the previous mode.

§Initial schema generation and regeneration

To output a generated schema, temporarily add a generate parameter or a regenerate parameter, after the fixed or backwards_compatible parameter, and then run the created test. If using Rust Analyzer this can be run from the IDE, or it can be run via cargo test.

To protect against accidentally doing the wrong thing, generate can only be used for initial generation, whereas regenerate can only be used for replacing an existing generation.

If a “FILE:..” path is specified, it will (re)generate that file, else it will output to the console:

  • In fixed mode, this will (re)generate against the given schema location.
  • In backwards_compatible mode, this will (re)generate against the latest schema location (the last in the list).

The test will then panic to ensure it fails, and can’t be left accidentally in (re)generate state.

#[derive(ScryptoSbor, ScryptoSborAssertion)]
#[sbor_assert(fixed("FILE:MyType-schema-v1.bin"), generate)]
struct MyType {
    // ...
}

§Fixed schema verification

To verify the type’s schema is unchanged, do:

#[derive(ScryptoSbor, ScryptoSborAssertion)]
#[sbor_assert(fixed("FILE:MyType-schema-v1.bin"))]
struct MyType {
    // ...
}

Instead of "FILE:X", you can also use "INLINE:<hex>", "CONST:<Constant>" or "EXPR:<Expression>" where the expression (such as generate_schema()) has to generate a SingleTypeSchema<NoCustomSchema>.

§Backwards compatibility verification

To allow multiple backwards-compatible versions, you can do this:

#[derive(ScryptoSbor, ScryptoSborAssertion)]
#[sbor_assert(backwards_compatible(
    version1 = "FILE:MyType-schema-v1.bin",
    version2 = "FILE:MyType-schema-v2.bin",
))]
struct MyType {
    // ...
}

Instead of "FILE:X.bin", you can also use "FILE:X.txt", "INLINE:<hex>", "CONST:<Constant>" or "EXPR:<Expression>" where the expression (such as generate_schema()) has to generate a SingleTypeSchema<ScryptoCustomSchema>.

If you wish to configure exactly which schemas are used for comparison of the current schema with the latest named schema; and each named schema with its predecessor, you can use:

#[derive(ScryptoSbor, ScryptoSborAssertion)]
#[sbor_assert(backwards_compatible("EXPR:<Expression>"))
struct MyType {
    // ...
}

Where the expression (such as params_builder()) has to generate a SingleTypeSchemaCompatibilityParameters<ScryptoCustomSchema>.

§Custom settings

By default, the fixed mode will use SchemaComparisonSettings::require_equality() and the backwards_compatible mode will use SchemaComparisonSettings::require_equality() for the check of current aginst the latest version, and SchemaComparisonSettings::allow_extension() for the checks between consecutive versions.

You may wish to change these:

  • If you just wish to ignore the equality of metadata such as names, you can use the allow_name_changes flag.
  • If you wish to override all settings, you can provide a constant containing your own SchemaComparisonSettings.
  • If you wish to specify a builder for settings, you can provide "EXPR:|builder| builder.<stuff>"
  • If for backwards_compatible, you wish to provide a separate configuration for the “latest” and “named versions” checks, you can use settings(comparison_between_versions = \"EXPR:F1\", comparison_between_current_and_latest = \"EXPR:F2\")

For example:

#[derive(ScryptoSbor, ScryptoSborAssertion)]
#[sbor_assert(
    fixed("FILE:MyType-schema-v1.bin"),
    settings(allow_name_changes),
)]
struct MyType {
    // ...
}

#[derive(ScryptoSbor, ScryptoSborAssertion)]
#[sbor_assert(
    backwards_compatible(
        v1 = "FILE:MyType-schema-v1.bin",
        v2 = "FILE:MyType-schema-v2.bin",
    ),
    settings(
        // We allow name changes between versions, but require the current schema to exactly match
        // the latest version (v2 in this case).
        // This could be useful to e.g. ensure that we have a fixed schema with the latest naming available.
        comparison_between_versions = "EXPR: |s| s.allow_all_name_changes()",
        comparison_between_current_and_latest = "EXPR: |s| s",
    ),
)]
struct MyType {
    // ...
}