Skip to content

Commit

Permalink
Merge pull request #2981 from onflow/bastian/type-value-migration
Browse files Browse the repository at this point in the history
State migration for type values with two or more interfaces
  • Loading branch information
SupunS authored Dec 22, 2023
2 parents 26fab85 + 9ff41a4 commit 354550f
Show file tree
Hide file tree
Showing 8 changed files with 1,250 additions and 23 deletions.
8 changes: 4 additions & 4 deletions migrations/account_type/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/interpreter"
"github.com/onflow/cadence/runtime/tests/runtime_utils"
. "github.com/onflow/cadence/runtime/tests/runtime_utils"
"github.com/onflow/cadence/runtime/tests/utils"
)

Expand Down Expand Up @@ -321,7 +321,7 @@ func TestTypeValueMigration(t *testing.T) {

// Store values

ledger := runtime_utils.NewTestLedger(nil, nil)
ledger := NewTestLedger(nil, nil)
storage := runtime.NewStorage(ledger, nil)

inter, err := interpreter.NewInterpreter(
Expand Down Expand Up @@ -451,7 +451,7 @@ func TestNestedTypeValueMigration(t *testing.T) {
expectedAccountTypeValue := interpreter.NewTypeValue(nil, unauthorizedAccountReferenceType)
stringTypeValue := interpreter.NewTypeValue(nil, interpreter.PrimitiveStaticTypeString)

ledger := runtime_utils.NewTestLedger(nil, nil)
ledger := NewTestLedger(nil, nil)
storage := runtime.NewStorage(ledger, nil)
locationRange := interpreter.EmptyLocationRange

Expand Down Expand Up @@ -728,7 +728,7 @@ func TestValuesWithStaticTypeMigration(t *testing.T) {
expectedValue interpreter.Value
}

ledger := runtime_utils.NewTestLedger(nil, nil)
ledger := NewTestLedger(nil, nil)
storage := runtime.NewStorage(ledger, nil)
locationRange := interpreter.EmptyLocationRange

Expand Down
56 changes: 56 additions & 0 deletions migrations/legacy_intersection_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Cadence - The resource-oriented smart contract programming language
*
* Copyright Dapper Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package migrations

import (
"strings"

"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/interpreter"
)

// LegacyIntersectionType simulates the old, incorrect restricted-type type-ID generation,
// which did not sort the type IDs of the interface types.
type LegacyIntersectionType struct {
*interpreter.IntersectionStaticType
}

var _ interpreter.StaticType = &LegacyIntersectionType{}

func (t *LegacyIntersectionType) ID() common.TypeID {
interfaceTypeIDs := make([]string, 0, len(t.Types))
for _, interfaceType := range t.Types {
interfaceTypeIDs = append(
interfaceTypeIDs,
string(interfaceType.ID()),
)
}

var result strings.Builder
result.WriteByte('{')
// NOTE: no sorting
for i, interfaceTypeID := range interfaceTypeIDs {
if i > 0 {
result.WriteByte(',')
}
result.WriteString(interfaceTypeID)
}
result.WriteByte('}')
return common.TypeID(result.String())
}
110 changes: 96 additions & 14 deletions migrations/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func (m *StorageMigration) migrateNestedValue(
for _, fieldName := range fieldNames {
existingValue := composite.GetField(
m.interpreter,
interpreter.EmptyLocationRange,
emptyLocationRange,
fieldName,
)

Expand Down Expand Up @@ -197,19 +197,29 @@ func (m *StorageMigration) migrateNestedValue(
case *interpreter.DictionaryValue:
dictionary := value

type keyValuePair struct {
key, value interpreter.Value
}

// Read the keys first, so the iteration wouldn't be affected
// by the modification of the nested values.
var existingKeys []interpreter.Value
dictionary.IterateKeys(m.interpreter, func(key interpreter.Value) (resume bool) {
existingKeys = append(existingKeys, key)
var existingKeysAndValues []keyValuePair
dictionary.Iterate(m.interpreter, func(key, value interpreter.Value) (resume bool) {
existingKeysAndValues = append(
existingKeysAndValues,
keyValuePair{
key: key,
value: value,
},
)

// continue iteration
return true
})

for _, existingKey := range existingKeys {
existingValue, exist := dictionary.Get(nil, interpreter.EmptyLocationRange, existingKey)
if !exist {
panic(errors.NewUnreachableError())
}
for _, existingKeyAndValue := range existingKeysAndValues {
existingKey := existingKeyAndValue.key
existingValue := existingKeyAndValue.value

newKey := m.migrateNestedValue(
addressPath,
Expand Down Expand Up @@ -238,11 +248,17 @@ func (m *StorageMigration) migrateNestedValue(
// Key was migrated.
// Remove the old value at the old key.
// This old value will be inserted again with the new key, unless the value is also migrated.
_ = dictionary.RemoveKey(
existingKey = legacyKey(existingKey)
oldValue := dictionary.Remove(
m.interpreter,
emptyLocationRange,
existingKey,
)

if _, ok := oldValue.(*interpreter.SomeValue); !ok {
panic(errors.NewUnreachableError())
}

keyToSet = newKey
}

Expand All @@ -253,10 +269,7 @@ func (m *StorageMigration) migrateNestedValue(
valueToSet = newValue
}

// Always wrap with an optional, when inserting to the dictionary.
valueToSet = interpreter.NewUnmeteredSomeValueNonCopying(valueToSet)

dictionary.SetKey(
dictionary.Insert(
m.interpreter,
emptyLocationRange,
keyToSet,
Expand Down Expand Up @@ -289,3 +302,72 @@ func (m *StorageMigration) migrateNestedValue(
return
}
}

// legacyKey return the same type with the "old" hash/ID generation algo.
func legacyKey(key interpreter.Value) interpreter.Value {
typeValue, isTypeValue := key.(interpreter.TypeValue)
if !isTypeValue {
return key
}

legacyType := legacyType(typeValue.Type)
if legacyType == nil {
return key
}

return interpreter.NewUnmeteredTypeValue(legacyType)
}

func legacyType(staticType interpreter.StaticType) interpreter.StaticType {
switch typ := staticType.(type) {
case *interpreter.IntersectionStaticType:
return &LegacyIntersectionType{
IntersectionStaticType: typ,
}

case *interpreter.ConstantSizedStaticType:
legacyType := legacyType(typ.Type)
if legacyType != nil {
return interpreter.NewConstantSizedStaticType(nil, legacyType, typ.Size)
}

case *interpreter.VariableSizedStaticType:
legacyType := legacyType(typ.Type)
if legacyType != nil {
return interpreter.NewVariableSizedStaticType(nil, legacyType)
}

case *interpreter.DictionaryStaticType:
legacyKeyType := legacyType(typ.KeyType)
legacyValueType := legacyType(typ.ValueType)
if legacyKeyType != nil && legacyValueType != nil {
return interpreter.NewDictionaryStaticType(nil, legacyKeyType, legacyValueType)
}
if legacyKeyType != nil {
return interpreter.NewDictionaryStaticType(nil, legacyKeyType, typ.ValueType)
}
if legacyValueType != nil {
return interpreter.NewDictionaryStaticType(nil, typ.KeyType, legacyValueType)
}

case *interpreter.OptionalStaticType:
legacyInnerType := legacyType(typ.Type)
if legacyInnerType != nil {
return interpreter.NewOptionalStaticType(nil, legacyInnerType)
}

case *interpreter.CapabilityStaticType:
legacyBorrowType := legacyType(typ.BorrowType)
if legacyBorrowType != nil {
return interpreter.NewCapabilityStaticType(nil, legacyBorrowType)
}

case *interpreter.ReferenceStaticType:
legacyReferencedType := legacyType(typ.ReferencedType)
if legacyReferencedType != nil {
return interpreter.NewReferenceStaticType(nil, typ.Authorization, legacyReferencedType)
}
}

return nil
}
4 changes: 2 additions & 2 deletions migrations/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/interpreter"
"github.com/onflow/cadence/runtime/tests/runtime_utils"
. "github.com/onflow/cadence/runtime/tests/runtime_utils"
"github.com/onflow/cadence/runtime/tests/utils"
)

Expand Down Expand Up @@ -106,7 +106,7 @@ func TestMultipleMigrations(t *testing.T) {
expectedValue interpreter.Value
}

ledger := runtime_utils.NewTestLedger(nil, nil)
ledger := NewTestLedger(nil, nil)
storage := runtime.NewStorage(ledger, nil)
locationRange := interpreter.EmptyLocationRange

Expand Down
4 changes: 2 additions & 2 deletions migrations/string_normalization/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/interpreter"
"github.com/onflow/cadence/runtime/tests/runtime_utils"
. "github.com/onflow/cadence/runtime/tests/runtime_utils"
"github.com/onflow/cadence/runtime/tests/utils"
)

Expand All @@ -44,7 +44,7 @@ func TestStringNormalizingMigration(t *testing.T) {
expectedValue interpreter.Value
}

ledger := runtime_utils.NewTestLedger(nil, nil)
ledger := NewTestLedger(nil, nil)
storage := runtime.NewStorage(ledger, nil)
locationRange := interpreter.EmptyLocationRange

Expand Down
Loading

0 comments on commit 354550f

Please sign in to comment.