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

Entitlements Migration #2951

Merged
merged 56 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
c046e28
include interface conformances when computing supported entitlements …
dsainati1 Aug 1, 2023
a42e1dd
compute entitled types
dsainati1 Aug 3, 2023
9f317a5
Merge branch 'sainati/interface-inherited-entitlement-sets' into sain…
dsainati1 Aug 3, 2023
dbc8038
tests for convert to entitled type
dsainati1 Aug 3, 2023
e5f4d47
add todo
dsainati1 Aug 3, 2023
63c6550
basic conversion of values
dsainati1 Aug 7, 2023
0791955
tests for arrays and dicts of references
dsainati1 Aug 7, 2023
1c0e20d
add test for capability values
dsainati1 Aug 8, 2023
b031af9
convert runtime type values
dsainati1 Aug 8, 2023
bfed26b
make TypeValue a pointer type
dsainati1 Aug 9, 2023
bb65048
fix tests
dsainati1 Aug 9, 2023
f5be50c
Merge branch 'feature/stable-cadence' of github.com:onflow/cadence in…
dsainati1 Aug 9, 2023
02bc9d5
Merge branch 'sainati/entitlement-sets' of github.com:onflow/cadence …
dsainati1 Aug 9, 2023
fa70004
removed old capability value
dsainati1 Aug 9, 2023
ba63ef1
add entitlements migration
dsainati1 Nov 16, 2023
a2f63c1
Merge branch 'sainati/supported-entitlements-interface' of github.com…
dsainati1 Nov 16, 2023
4ecf451
add implementation and tests for ConvertEntitledType
dsainati1 Nov 16, 2023
6916901
Merge branch 'sainati/convert-entitled-value' of github.com:onflow/ca…
dsainati1 Nov 16, 2023
7533eef
add tests for array and dict values
dsainati1 Nov 17, 2023
6caeeec
convert array and dict types
dsainati1 Nov 17, 2023
4e72efc
scaffolding for capabilities and storagereferences
dsainati1 Nov 20, 2023
332646a
Merge branch 'feature/stable-cadence' of github.com:onflow/cadence in…
dsainati1 Nov 20, 2023
be44520
support for migrating legacy intersection types
dsainati1 Nov 21, 2023
26688cc
test for simple migration
dsainati1 Nov 21, 2023
22aab6f
Merge branch 'supun/generalized-migration' of https://github.com/onfl…
SupunS Nov 21, 2023
c9a78f5
add test for full update flow
dsainati1 Nov 27, 2023
6193e44
improve test
dsainati1 Nov 27, 2023
f201835
also walk container static types when removing restricted types
dsainati1 Dec 1, 2023
c465ab0
fix test
dsainati1 Dec 4, 2023
c2c0098
Merge branch 'master' of github.com:onflow/cadence into sainati/entit…
dsainati1 Dec 11, 2023
6aaa023
update convertValue to create a new value
dsainati1 Dec 11, 2023
e867648
adjust tests
dsainati1 Dec 12, 2023
af22600
add test for array of values
dsainati1 Dec 13, 2023
591471d
broken commit for testing
dsainati1 Dec 13, 2023
cb3d934
add new test
dsainati1 Dec 13, 2023
cf9f801
return nil for unchanged values
dsainati1 Dec 13, 2023
e0958b3
Merge branch 'test-migration-branch' of github.com:onflow/cadence int…
dsainati1 Dec 13, 2023
600eafc
fix comment
dsainati1 Dec 13, 2023
59e9af8
respond to review
dsainati1 Dec 13, 2023
9ebd416
revert change of TypeValue to pointer type
dsainati1 Dec 15, 2023
0afbaea
remove unnecessary formatting changes
dsainati1 Dec 15, 2023
f187329
remove unnecessary formatting changes
dsainati1 Dec 15, 2023
fb88f49
Update migrations/migration.go
dsainati1 Dec 20, 2023
444ad83
Merge branch 'master' of github.com:onflow/cadence into sainati/entit…
dsainati1 Jan 2, 2024
1413852
Merge branch 'master' of github.com:onflow/cadence into sainati/entit…
dsainati1 Jan 5, 2024
c46d84e
don't create new wrapper types when inner type does not change
dsainati1 Jan 5, 2024
f5f26f6
add comment
dsainati1 Jan 5, 2024
de138fa
add test for typeValue keys
dsainati1 Jan 5, 2024
1df8f05
Merge branch 'master' of github.com:onflow/cadence into sainati/entit…
dsainati1 Jan 10, 2024
757dacb
Merge branch 'master' into sainati/entitlements-migration
turbolent Jan 12, 2024
8d75305
Merge branch 'master' of github.com:onflow/cadence into sainati/entit…
dsainati1 Jan 12, 2024
c2bb44d
fix compile
dsainati1 Jan 12, 2024
63f5cde
Merge branch 'sainati/entitlements-migration' of github.com:onflow/ca…
dsainati1 Jan 12, 2024
96c87d5
Update migrations/migration.go
dsainati1 Jan 16, 2024
4a4ba6b
fix lint
dsainati1 Jan 16, 2024
aabab60
fix lint
dsainati1 Jan 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
274 changes: 274 additions & 0 deletions migrations/entitlements/migration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
/*

Check failure on line 1 in migrations/entitlements/migration.go

View workflow job for this annotation

GitHub Actions / Lint

: # github.com/onflow/cadence/migrations/entitlements [github.com/onflow/cadence/migrations/entitlements.test]
* 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 entitlements

import (
"github.com/onflow/cadence/migrations"
"github.com/onflow/cadence/runtime/interpreter"
"github.com/onflow/cadence/runtime/sema"
)

type EntitlementsMigration struct {
Interpreter *interpreter.Interpreter
}

var _ migrations.ValueMigration = EntitlementsMigration{}

func NewEntitlementsMigration(inter *interpreter.Interpreter) EntitlementsMigration {
return EntitlementsMigration{Interpreter: inter}
}

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

// Converts its input to an entitled type according to the following rules:
// * `ConvertToEntitledType(&T) ---> auth(Entitlements(T)) &T`
// * `ConvertToEntitledType(Capability<T>) ---> Capability<ConvertToEntitledType(T)>`
// * `ConvertToEntitledType(T?) ---> ConvertToEntitledType(T)?
// * `ConvertToEntitledType(T) ---> T`
// where Entitlements(I) is defined as the result of T.SupportedEntitlements()
func ConvertToEntitledType(t sema.Type) (sema.Type, bool) {
switch t := t.(type) {
case *sema.ReferenceType:
switch t.Authorization {
case sema.UnauthorizedAccess:
innerType, convertedInner := ConvertToEntitledType(t.Type)
auth := sema.UnauthorizedAccess
if entitlementSupportingType, ok := innerType.(sema.EntitlementSupportingType); ok {
supportedEntitlements := entitlementSupportingType.SupportedEntitlements()
if supportedEntitlements.Len() > 0 {
auth = sema.EntitlementSetAccess{
SetKind: sema.Conjunction,
Entitlements: supportedEntitlements,
}
}
}
if auth.Equal(sema.UnauthorizedAccess) && !convertedInner {
return t, false
}
return sema.NewReferenceType(
nil,
auth,
innerType,
), true
// type is already entitled
default:
return t, false
}
case *sema.OptionalType:
ty, converted := ConvertToEntitledType(t.Type)
if !converted {
return t, false
}
return sema.NewOptionalType(nil, ty), true
case *sema.CapabilityType:
ty, converted := ConvertToEntitledType(t.BorrowType)
if !converted {
return t, false
}
return sema.NewCapabilityType(nil, ty), true
case *sema.VariableSizedType:
ty, converted := ConvertToEntitledType(t.Type)
if !converted {
return t, false
}
return sema.NewVariableSizedType(nil, ty), true
case *sema.ConstantSizedType:
ty, converted := ConvertToEntitledType(t.Type)
if !converted {
return t, false
}
return sema.NewConstantSizedType(nil, ty, t.Size), true
case *sema.DictionaryType:
keyTy, convertedKey := ConvertToEntitledType(t.KeyType)
valueTy, convertedValue := ConvertToEntitledType(t.ValueType)
if !convertedKey && !convertedValue {
return t, false
}
return sema.NewDictionaryType(nil, keyTy, valueTy), true
default:
return t, false
}
}

// Converts the input value into a version compatible with the new entitlements feature,
// with the same members/operations accessible on any references as would have been accessible in the past.
func ConvertValueToEntitlements(
inter *interpreter.Interpreter,
v interpreter.Value,
) interpreter.Value {

var staticType interpreter.StaticType
// during a real migration these two reference cases will not be hit, but they are here for easier testing
// for reference types, we want to use the borrow type, rather than the type of the referenced value
switch referenceValue := v.(type) {
case *interpreter.EphemeralReferenceValue:
staticType = interpreter.NewReferenceStaticType(
inter,
referenceValue.Authorization,
interpreter.ConvertSemaToStaticType(inter, referenceValue.BorrowedType),
)
case *interpreter.StorageReferenceValue:
staticType = interpreter.NewReferenceStaticType(
inter,
referenceValue.Authorization,
interpreter.ConvertSemaToStaticType(inter, referenceValue.BorrowedType),
)
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
default:
staticType = v.StaticType(inter)
}

// if the static type contains a legacy restricted type, convert it to a new type according to some rules:
// &T{I} -> auth(SupportedEntitlements(I)) &T
// Capability<&T{I}> -> Capability<auth(SupportedEntitlements(I)) &T>
var convertLegacyStaticType func(interpreter.StaticType)
convertLegacyStaticType = func(staticType interpreter.StaticType) {
switch t := staticType.(type) {
case *interpreter.ReferenceStaticType:
switch referencedType := t.ReferencedType.(type) {
case *interpreter.IntersectionStaticType:
if referencedType.LegacyType != nil {
t.ReferencedType = referencedType.LegacyType
intersectionSemaType := inter.MustConvertStaticToSemaType(referencedType).(*sema.IntersectionType)
auth := sema.UnauthorizedAccess
supportedEntitlements := intersectionSemaType.SupportedEntitlements()
if supportedEntitlements.Len() > 0 {
auth = sema.EntitlementSetAccess{
SetKind: sema.Conjunction,
Entitlements: supportedEntitlements,
}
}
t.Authorization = interpreter.ConvertSemaAccessToStaticAuthorization(inter, auth)
}
}
case *interpreter.CapabilityStaticType:
convertLegacyStaticType(t.BorrowType)
case *interpreter.VariableSizedStaticType:
convertLegacyStaticType(t.Type)
case *interpreter.ConstantSizedStaticType:
convertLegacyStaticType(t.Type)
case *interpreter.DictionaryStaticType:
convertLegacyStaticType(t.KeyType)
convertLegacyStaticType(t.ValueType)
case *interpreter.OptionalStaticType:
convertLegacyStaticType(t.Type)
}
SupunS marked this conversation as resolved.
Show resolved Hide resolved
}

convertLegacyStaticType(staticType)
semaType := inter.MustConvertStaticToSemaType(staticType)
entitledType, converted := ConvertToEntitledType(semaType)

// if the types of the values are equal and the value is not a runtime type, there's nothing to migrate
if !converted && !entitledType.Equal(sema.MetaType) {
return nil
}

switch v := v.(type) {
// during a real migration these two reference cases will not be hit, but they are here for easier testing
case *interpreter.EphemeralReferenceValue:
entitledReferenceType := entitledType.(*sema.ReferenceType)
staticAuthorization := interpreter.ConvertSemaAccessToStaticAuthorization(inter, entitledReferenceType.Authorization)
convertedValue := ConvertValueToEntitlements(inter, v.Value)
// if the underlying value did not change, we still want to use the old value in the newly created reference
if convertedValue == nil {
convertedValue = v.Value
}
return interpreter.NewEphemeralReferenceValue(
inter,
staticAuthorization,
convertedValue,
entitledReferenceType.Type,
interpreter.EmptyLocationRange,
)

case *interpreter.StorageReferenceValue:
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
// a stored value will in itself be migrated at another point, so no need to do anything here other than change the type
entitledReferenceType := entitledType.(*sema.ReferenceType)
staticAuthorization := interpreter.ConvertSemaAccessToStaticAuthorization(inter, entitledReferenceType.Authorization)
return interpreter.NewStorageReferenceValue(
inter,
staticAuthorization,
v.TargetStorageAddress,
v.TargetPath,
entitledReferenceType.Type,
)

case *interpreter.ArrayValue:
entitledArrayType := entitledType.(sema.ArrayType)
arrayStaticType := interpreter.ConvertSemaArrayTypeToStaticArrayType(inter, entitledArrayType)

iterator := v.Iterator(inter)

Check failure on line 218 in migrations/entitlements/migration.go

View workflow job for this annotation

GitHub Actions / Performance regression check

not enough arguments in call to v.Iterator

Check failure on line 218 in migrations/entitlements/migration.go

View workflow job for this annotation

GitHub Actions / Lint

not enough arguments in call to v.Iterator

Check failure on line 218 in migrations/entitlements/migration.go

View workflow job for this annotation

GitHub Actions / Lint

not enough arguments in call to v.Iterator

Check failure on line 218 in migrations/entitlements/migration.go

View workflow job for this annotation

GitHub Actions / Test

not enough arguments in call to v.Iterator

newArray := interpreter.NewArrayValueWithIterator(inter, arrayStaticType, v.GetOwner(), uint64(v.Count()), func() interpreter.Value {
return iterator.Next(inter)

Check failure on line 221 in migrations/entitlements/migration.go

View workflow job for this annotation

GitHub Actions / Performance regression check

not enough arguments in call to iterator.Next

Check failure on line 221 in migrations/entitlements/migration.go

View workflow job for this annotation

GitHub Actions / Lint

not enough arguments in call to iterator.Next

Check failure on line 221 in migrations/entitlements/migration.go

View workflow job for this annotation

GitHub Actions / Lint

not enough arguments in call to iterator.Next

Check failure on line 221 in migrations/entitlements/migration.go

View workflow job for this annotation

GitHub Actions / Test

not enough arguments in call to iterator.Next
})
return newArray

case *interpreter.DictionaryValue:
entitledDictionaryType := entitledType.(*sema.DictionaryType)
dictionaryStaticType := interpreter.ConvertSemaDictionaryTypeToStaticDictionaryType(inter, entitledDictionaryType)

var values []interpreter.Value

v.Iterate(inter, func(key, value interpreter.Value) (resume bool) {
values = append(values, key)
values = append(values, value)
return true
})

newDict := interpreter.NewDictionaryValue(
inter,
interpreter.EmptyLocationRange,
dictionaryStaticType,
values...,
)
return newDict
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved

case *interpreter.CapabilityValue:
// capabilities should just have their borrow type updated, as the pointed-to value will also be visited
// by the migration on its own
entitledCapabilityValue := entitledType.(*sema.CapabilityType)
capabilityStaticType := interpreter.ConvertSemaToStaticType(inter, entitledCapabilityValue.BorrowType)
return interpreter.NewCapabilityValue(inter, v.ID, v.Address, capabilityStaticType)

case interpreter.TypeValue:
if v.Type == nil {
return nil
}

convertedType, _ := ConvertToEntitledType(
inter.MustConvertStaticToSemaType(v.Type),
)

// convert the static type of the value
entitledStaticType := interpreter.ConvertSemaToStaticType(
inter,
convertedType,
)
return interpreter.NewTypeValue(inter, entitledStaticType)
}

return nil
}

func (mig EntitlementsMigration) Migrate(_ interpreter.AddressPath, value interpreter.Value, _ *interpreter.Interpreter) (interpreter.Value, error) {
return ConvertValueToEntitlements(mig.Interpreter, value), nil
}
Loading
Loading