Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix migrating values with empty intersection type #3138

Merged
merged 13 commits into from
Mar 4, 2024
8 changes: 6 additions & 2 deletions migrations/entitlements/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ func TestConvertToEntitledType(t *testing.T) {
nil,
sema.UnauthorizedAccess,
sema.NewIntersectionType(
nil,
nil,
[]*sema.InterfaceType{
interfaceTypeInheriting,
Expand All @@ -541,6 +542,7 @@ func TestConvertToEntitledType(t *testing.T) {
nil,
eFAndGAccess,
sema.NewIntersectionType(
nil,
nil,
[]*sema.InterfaceType{
interfaceTypeInheriting,
Expand All @@ -556,6 +558,7 @@ func TestConvertToEntitledType(t *testing.T) {
sema.NewOptionalType(
nil,
sema.NewIntersectionType(
nil,
nil,
[]*sema.InterfaceType{
interfaceTypeInheriting,
Expand All @@ -570,6 +573,7 @@ func TestConvertToEntitledType(t *testing.T) {
sema.NewOptionalType(
nil,
sema.NewIntersectionType(
nil,
nil,
[]*sema.InterfaceType{
interfaceTypeInheriting,
Expand Down Expand Up @@ -1313,7 +1317,7 @@ func TestConvertToEntitledValue(t *testing.T) {
},
}

test := func(testCase testCase, valueGenerator valueGenerator, typeGenerator typeGenerator) {
test := func(t *testing.T, testCase testCase, valueGenerator valueGenerator, typeGenerator typeGenerator) {

input := valueGenerator.wrap(typeGenerator.wrap(testCase.Input))
if input == nil {
Expand Down Expand Up @@ -1346,7 +1350,7 @@ func TestConvertToEntitledValue(t *testing.T) {
for _, typeGenerator := range typeGenerators {
t.Run(typeGenerator.name, func(t *testing.T) {

test(testCase, valueGenerator, typeGenerator)
test(t, testCase, valueGenerator, typeGenerator)
})
}
})
Expand Down
217 changes: 203 additions & 14 deletions migrations/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/interpreter"
"github.com/onflow/cadence/runtime/sema"
"github.com/onflow/cadence/runtime/stdlib"
. "github.com/onflow/cadence/runtime/tests/runtime_utils"
"github.com/onflow/cadence/runtime/tests/utils"
Expand All @@ -42,10 +43,10 @@ type testReporter struct {
interpreter.StorageKey
interpreter.StorageMapKey
}][]string
errored map[struct {
errors map[struct {
interpreter.StorageKey
interpreter.StorageMapKey
}][]string
}][]error
}

var _ Reporter = &testReporter{}
Expand All @@ -56,10 +57,10 @@ func newTestReporter() *testReporter {
interpreter.StorageKey
interpreter.StorageMapKey
}][]string{},
errored: map[struct {
errors: map[struct {
interpreter.StorageKey
interpreter.StorageMapKey
}][]string{},
}][]error{},
}
}

Expand All @@ -85,8 +86,8 @@ func (t *testReporter) Migrated(
func (t *testReporter) Error(
storageKey interpreter.StorageKey,
storageMapKey interpreter.StorageMapKey,
migration string,
_ error,
_ string,
err error,
) {
key := struct {
interpreter.StorageKey
Expand All @@ -96,9 +97,9 @@ func (t *testReporter) Error(
StorageMapKey: storageMapKey,
}

t.errored[key] = append(
t.errored[key],
migration,
t.errors[key] = append(
t.errors[key],
err,
)
}

Expand Down Expand Up @@ -702,18 +703,18 @@ func TestMigrationError(t *testing.T) {
map[struct {
interpreter.StorageKey
interpreter.StorageMapKey
}][]string{
}][]error{
{
StorageKey: interpreter.StorageKey{
Address: account,
Key: pathDomain.Identifier(),
},
StorageMapKey: interpreter.StringStorageMapKey("int8_value"),
}: {
"testInt8Migration",
errors.New("error occurred while migrating int8"),
},
},
reporter.errored,
reporter.errors,
)
}

Expand Down Expand Up @@ -806,7 +807,7 @@ func TestCapConMigration(t *testing.T) {

// Assert

assert.Len(t, reporter.errored, 0)
assert.Len(t, reporter.errors, 0)
assert.Len(t, reporter.migrated, 2)

storageMap = storage.GetStorageMap(
Expand Down Expand Up @@ -921,7 +922,7 @@ func TestContractMigration(t *testing.T) {

// Assert

assert.Len(t, reporter.errored, 0)
assert.Len(t, reporter.errors, 0)
assert.Len(t, reporter.migrated, 1)

value, err := rt.ExecuteScript(
Expand All @@ -947,3 +948,191 @@ func TestContractMigration(t *testing.T) {
value,
)
}

// testCompositeValueMigration

type testCompositeValueMigration struct {
}

var _ ValueMigration = testCompositeValueMigration{}

func (testCompositeValueMigration) Name() string {
return "testCompositeValueMigration"
}

func (m testCompositeValueMigration) Migrate(
_ interpreter.StorageKey,
_ interpreter.StorageMapKey,
value interpreter.Value,
inter *interpreter.Interpreter,
) (
interpreter.Value,
error,
) {
compositeValue, ok := value.(*interpreter.CompositeValue)
if !ok {
return nil, nil
}

return interpreter.NewCompositeValue(
inter,
emptyLocationRange,
utils.TestLocation,
"S2",
common.CompositeKindStructure,
nil,
common.Address(compositeValue.StorageAddress()),
), nil
}

func TestEmptyIntersectionTypeMigration(t *testing.T) {

t.Parallel()

testAddress := common.MustBytesToAddress([]byte{0x1})

rt := NewTestInterpreterRuntime()

runtimeInterface := &TestRuntimeInterface{
Storage: NewTestLedger(nil, nil),
}

// Prepare

storage, inter, err := rt.Storage(runtime.Context{
Location: utils.TestLocation,
Interface: runtimeInterface,
})
require.NoError(t, err)

storageMap := storage.GetStorageMap(
testAddress,
common.PathDomainStorage.Identifier(),
true,
)

elaboration := sema.NewElaboration(nil)

const s1QualifiedIdentifier = "S1"
const s2QualifiedIdentifier = "S2"

elaboration.SetCompositeType(
utils.TestLocation.TypeID(nil, s1QualifiedIdentifier),
&sema.CompositeType{
Location: utils.TestLocation,
Members: &sema.StringMemberOrderedMap{},
Identifier: s1QualifiedIdentifier,
Kind: common.CompositeKindStructure,
},
)

elaboration.SetCompositeType(
utils.TestLocation.TypeID(nil, s2QualifiedIdentifier),
&sema.CompositeType{
Location: utils.TestLocation,
Members: &sema.StringMemberOrderedMap{},
Identifier: s2QualifiedIdentifier,
Kind: common.CompositeKindStructure,
},
)

compositeValue := interpreter.NewCompositeValue(
inter,
emptyLocationRange,
utils.TestLocation,
s1QualifiedIdentifier,
common.CompositeKindStructure,
nil,
testAddress,
)

inter.Program = &interpreter.Program{
Elaboration: elaboration,
}

// NOTE: create an empty intersection type with a legacy type: AnyStruct{}
emptyIntersectionType := interpreter.NewIntersectionStaticType(
nil,
nil,
)
emptyIntersectionType.LegacyType = interpreter.PrimitiveStaticTypeAnyStruct

storageMapKey := interpreter.StringStorageMapKey("test")

dictionaryKey := interpreter.NewUnmeteredStringValue("foo")

dictionaryValue := interpreter.NewDictionaryValueWithAddress(
inter,
emptyLocationRange,
interpreter.NewDictionaryStaticType(
nil,
interpreter.PrimitiveStaticTypeString,
emptyIntersectionType,
),
testAddress,
)

dictionaryValue.Insert(
inter,
emptyLocationRange,
dictionaryKey,
compositeValue,
)

storageMap.WriteValue(
inter,
storageMapKey,
dictionaryValue,
)

// Migrate

reporter := newTestReporter()

migration := NewStorageMigration(inter, storage)

migration.Migrate(
&AddressSliceIterator{
Addresses: []common.Address{
testAddress,
},
},
migration.NewValueMigrationsPathMigrator(
reporter,
testCompositeValueMigration{},
),
)

err = migration.Commit()
require.NoError(t, err)

// Assert

assert.Len(t, reporter.errors, 0)
assert.Len(t, reporter.migrated, 1)

storageMap = storage.GetStorageMap(
testAddress,
common.PathDomainStorage.Identifier(),
false,
)

assert.Equal(t, uint64(1), storageMap.Count())

migratedValue := storageMap.ReadValue(nil, storageMapKey)

require.IsType(t, &interpreter.DictionaryValue{}, migratedValue)
migratedDictionaryValue := migratedValue.(*interpreter.DictionaryValue)

migratedChildValue, ok := migratedDictionaryValue.Get(inter, emptyLocationRange, dictionaryKey)
require.True(t, ok)

require.IsType(t, &interpreter.CompositeValue{}, migratedChildValue)
migratedCompositeValue := migratedChildValue.(*interpreter.CompositeValue)

require.Equal(
t,
s2QualifiedIdentifier,
migratedCompositeValue.QualifiedIdentifier,
)
}
14 changes: 5 additions & 9 deletions runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3321,7 +3321,7 @@ func lookupEntitlement(interpreter *Interpreter, typeID string) (*sema.Entitleme
return nil, err
}

typ, err := interpreter.getEntitlement(common.TypeID(typeID))
typ, err := interpreter.GetEntitlementType(common.TypeID(typeID))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -4473,7 +4473,7 @@ func (interpreter *Interpreter) authAccountCheckFunction(addressValue AddressVal
)
}

func (interpreter *Interpreter) getEntitlement(typeID common.TypeID) (*sema.EntitlementType, error) {
func (interpreter *Interpreter) GetEntitlementType(typeID common.TypeID) (*sema.EntitlementType, error) {
location, qualifiedIdentifier, err := common.DecodeTypeID(interpreter, string(typeID))
if err != nil {
return nil, err
Expand Down Expand Up @@ -4507,7 +4507,7 @@ func (interpreter *Interpreter) getEntitlement(typeID common.TypeID) (*sema.Enti
return ty, nil
}

func (interpreter *Interpreter) getEntitlementMapType(typeID common.TypeID) (*sema.EntitlementMapType, error) {
func (interpreter *Interpreter) GetEntitlementMapType(typeID common.TypeID) (*sema.EntitlementMapType, error) {
location, qualifiedIdentifier, err := common.DecodeTypeID(interpreter, string(typeID))
if err != nil {
return nil, err
Expand Down Expand Up @@ -4546,10 +4546,7 @@ func (interpreter *Interpreter) ConvertStaticToSemaType(staticType StaticType) (
return ConvertStaticToSemaType(
config.MemoryGauge,
staticType,
interpreter.GetInterfaceType,
interpreter.GetCompositeType,
interpreter.getEntitlement,
interpreter.getEntitlementMapType,
interpreter,
)
}

Expand All @@ -4569,8 +4566,7 @@ func (interpreter *Interpreter) MustConvertStaticAuthorizationToSemaAccess(auth
access, err := ConvertStaticAuthorizationToSemaAccess(
interpreter,
auth,
interpreter.getEntitlement,
interpreter.getEntitlementMapType,
interpreter,
)
if err != nil {
panic(err)
Expand Down
Loading
Loading