Skip to content

Commit

Permalink
Merge branch 'master' into feature/atree-register-inlining-v1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent committed May 15, 2024
2 parents 72871ab + e837800 commit f1e0bf3
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 19 deletions.
2 changes: 1 addition & 1 deletion npm-packages/cadence-parser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onflow/cadence-parser",
"version": "1.0.0-preview.27",
"version": "1.0.0-preview.28",
"description": "The Cadence parser",
"homepage": "https://github.com/onflow/cadence",
"repository": {
Expand Down
192 changes: 192 additions & 0 deletions runtime/stdlib/cadence_v0.42_to_v1_contract_upgrade_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,198 @@ func TestContractUpgradeFieldType(t *testing.T) {
err := testContractUpdate(t, oldCode, newCode)
require.NoError(t, err)
})

t.Run("custom type change, explicit AnyResource restricted type", func(t *testing.T) {

t.Parallel()

const oldCode = `
import MetadataViews from 0x02
access(all) contract Test {
access(all) resource interface Foo {}
access(all) var a: Capability<&AnyResource{MetadataViews.Resolver}>?
init() {
self.a = nil
}
}
`

const newImport = `
access(all) contract ViewResolver {
access(all) resource interface Resolver {}
}
`

const newCode = `
import ViewResolver from 0x02
access(all) contract Test {
access(all) resource interface Foo {}
access(all) var a: Capability<&{ViewResolver.Resolver}>?
init() {
self.a = nil
}
}
`

const contractName = "Test"
location := common.AddressLocation{
Name: contractName,
Address: common.MustBytesToAddress([]byte{0x1}),
}

metadataViewsLocation := common.AddressLocation{
Name: "MetadataViews",
Address: common.MustBytesToAddress([]byte{0x2}),
}

viewResolverLocation := common.AddressLocation{
Name: "ViewResolver",
Address: common.MustBytesToAddress([]byte{0x2}),
}

imports := map[common.Location]string{
viewResolverLocation: newImport,
}

oldProgram, newProgram, elaborations := parseAndCheckPrograms(t, location, oldCode, newCode, imports)

metadataViewsResolverTypeID := common.NewTypeIDFromQualifiedName(
nil,
metadataViewsLocation,
"MetadataViews.Resolver",
)

viewResolverResolverTypeID := common.NewTypeIDFromQualifiedName(
nil,
viewResolverLocation,
"ViewResolver.Resolver",
)

upgradeValidator := stdlib.NewCadenceV042ToV1ContractUpdateValidator(
location,
contractName,
&runtime_utils.TestRuntimeInterface{
OnGetAccountContractNames: func(address runtime.Address) ([]string, error) {
return []string{"TestImport"}, nil
},
},
oldProgram,
newProgram,
elaborations,
).WithUserDefinedTypeChangeChecker(
func(oldTypeID common.TypeID, newTypeID common.TypeID) (checked, valid bool) {
switch oldTypeID {
case metadataViewsResolverTypeID:
return true, newTypeID == viewResolverResolverTypeID
}

return false, false
},
)

err := upgradeValidator.Validate()
require.NoError(t, err)
})

t.Run("custom type change, implicit AnyResource restricted type", func(t *testing.T) {

t.Parallel()

const oldCode = `
import MetadataViews from 0x02
access(all) contract Test {
access(all) resource interface Foo {}
access(all) var a: Capability<&{MetadataViews.Resolver}>?
init() {
self.a = nil
}
}
`

const newImport = `
access(all) contract ViewResolver {
access(all) resource interface Resolver {}
}
`

const newCode = `
import ViewResolver from 0x02
access(all) contract Test {
access(all) resource interface Foo {}
access(all) var a: Capability<&{ViewResolver.Resolver}>?
init() {
self.a = nil
}
}
`

const contractName = "Test"
location := common.AddressLocation{
Name: contractName,
Address: common.MustBytesToAddress([]byte{0x1}),
}

metadataViewsLocation := common.AddressLocation{
Name: "MetadataViews",
Address: common.MustBytesToAddress([]byte{0x2}),
}

viewResolverLocation := common.AddressLocation{
Name: "ViewResolver",
Address: common.MustBytesToAddress([]byte{0x2}),
}

imports := map[common.Location]string{
viewResolverLocation: newImport,
}

oldProgram, newProgram, elaborations := parseAndCheckPrograms(t, location, oldCode, newCode, imports)

metadataViewsResolverTypeID := common.NewTypeIDFromQualifiedName(
nil,
metadataViewsLocation,
"MetadataViews.Resolver",
)

viewResolverResolverTypeID := common.NewTypeIDFromQualifiedName(
nil,
viewResolverLocation,
"ViewResolver.Resolver",
)

upgradeValidator := stdlib.NewCadenceV042ToV1ContractUpdateValidator(
location,
contractName,
&runtime_utils.TestRuntimeInterface{
OnGetAccountContractNames: func(address runtime.Address) ([]string, error) {
return []string{"TestImport"}, nil
},
},
oldProgram,
newProgram,
elaborations,
).WithUserDefinedTypeChangeChecker(
func(oldTypeID common.TypeID, newTypeID common.TypeID) (checked, valid bool) {
switch oldTypeID {
case metadataViewsResolverTypeID:
return true, newTypeID == viewResolverResolverTypeID
}

return false, false
},
)

err := upgradeValidator.Validate()
require.NoError(t, err)
})
}

func TestContractUpgradeIntersectionAuthorization(t *testing.T) {
Expand Down
45 changes: 28 additions & 17 deletions runtime/stdlib/cadence_v0.42_to_v1_contract_upgrade_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,12 +401,12 @@ var astFullyEntitledAccountReferenceType = &ast.ReferenceType{

func (validator *CadenceV042ToV1ContractUpdateValidator) checkTypeUpgradability(oldType ast.Type, newType ast.Type, inCapability bool) error {

typeSwitch:
switch oldType := oldType.(type) {
case *ast.OptionalType:
if newOptional, isOptional := newType.(*ast.OptionalType); isOptional {
return validator.checkTypeUpgradability(oldType.Type, newOptional.Type, inCapability)
}

case *ast.ReferenceType:

if newReference, isReference := newType.(*ast.ReferenceType); isReference {
Expand All @@ -432,10 +432,12 @@ typeSwitch:
return nil
}
}

case *ast.IntersectionType:
// If the intersection type have doesn't a legacy restricted type,
// the interface set must be compatible.
if oldType.LegacyRestrictedType == nil {
// or if the legacy restricted type is AnyStruct/AnyResource (i.e: {T} or AnyStruct{T} or AnyResource{T})
// then the interface set must be compatible.
if dropRestrictedType(oldType) {
newIntersectionType, ok := newType.(*ast.IntersectionType)
if !ok {
return newTypeMismatchError(oldType, newType)
Expand Down Expand Up @@ -483,20 +485,6 @@ typeSwitch:
// they must be upgraded according to the migration rules: i.e. R{I} -> R
validator.currentRestrictedTypeUpgradeRestrictions = oldType.Types

// If the old restricted type is for AnyStruct/AnyResource,
// and if there are atleast one restriction, require them to drop the "restricted type".
// e.g-1: `AnyStruct{I} -> {I}`
// e.g-2: `AnyResource{I} -> {I}`
// See: https://github.com/onflow/cadence/issues/3112
if restrictedNominalType, isNominal := oldType.LegacyRestrictedType.(*ast.NominalType); isNominal {
switch restrictedNominalType.Identifier.Identifier {
case "AnyStruct", "AnyResource":
if len(oldType.Types) > 0 {
break typeSwitch
}
}
}

// Otherwise require them to drop the "restrictions".
// e.g-1: `T{I} -> T`
// e.g-2: `AnyStruct{} -> AnyStruct`
Expand All @@ -506,6 +494,7 @@ typeSwitch:
if newVariableSizedType, isVariableSizedType := newType.(*ast.VariableSizedType); isVariableSizedType {
return validator.checkTypeUpgradability(oldType.Type, newVariableSizedType.Type, inCapability)
}

case *ast.ConstantSizedType:
if newConstantSizedType, isConstantSizedType := newType.(*ast.ConstantSizedType); isConstantSizedType {
if oldType.Size.Value.Cmp(newConstantSizedType.Size.Value) != 0 ||
Expand All @@ -514,6 +503,7 @@ typeSwitch:
}
return validator.checkTypeUpgradability(oldType.Type, newConstantSizedType.Type, inCapability)
}

case *ast.DictionaryType:
if newDictionaryType, isDictionaryType := newType.(*ast.DictionaryType); isDictionaryType {
err := validator.checkTypeUpgradability(oldType.KeyType, newDictionaryType.KeyType, inCapability)
Expand All @@ -522,6 +512,7 @@ typeSwitch:
}
return validator.checkTypeUpgradability(oldType.ValueType, newDictionaryType.ValueType, inCapability)
}

case *ast.InstantiationType:
// if the type is a Capability, allow the borrow type to change according to the normal upgrade rules
if oldNominalType, isNominal := oldType.Type.(*ast.NominalType); isNominal &&
Expand Down Expand Up @@ -628,6 +619,26 @@ typeSwitch:

}

func dropRestrictedType(intersectionType *ast.IntersectionType) bool {
if intersectionType.LegacyRestrictedType == nil {
return true
}

// If the old restricted type is for AnyStruct/AnyResource,
// and if there are atleast one restriction, require them to drop the "restricted type".
// e.g-1: `AnyStruct{I} -> {I}`
// e.g-2: `AnyResource{I} -> {I}`
// See: https://github.com/onflow/cadence/issues/3112
if restrictedNominalType, isNominal := intersectionType.LegacyRestrictedType.(*ast.NominalType); isNominal {
switch restrictedNominalType.Identifier.Identifier {
case "AnyStruct", "AnyResource":
return len(intersectionType.Types) > 0
}
}

return false
}

func (validator *CadenceV042ToV1ContractUpdateValidator) checkUserDefinedTypeCustomRules(
oldType ast.Type,
newType ast.Type,
Expand Down
2 changes: 1 addition & 1 deletion version.go

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

0 comments on commit f1e0bf3

Please sign in to comment.