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

Capability controllers migration #2912

Merged
merged 57 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
159ed58
export link to capability controller migration
turbolent May 17, 2023
82b5389
start work on cap cons migration: migrate links of accounts
turbolent May 18, 2023
d087fd1
migrate path capabilities which are stored in a storage map
turbolent May 20, 2023
49c7e3e
migrate composites, optionals, arrays, and dictionaries
turbolent Jun 20, 2023
4a6155a
Revert "use existing DeploymentTransaction helper"
turbolent Jun 20, 2023
ac2c791
clarify naming
turbolent Jun 20, 2023
393798b
add comments
turbolent Jun 20, 2023
d880408
bring back dependencies of cap cons migration
turbolent Sep 21, 2023
0d43b6f
add new return value
turbolent Oct 30, 2023
7335183
update tests to Cadence 1.0
turbolent Oct 30, 2023
de47719
bring back path capability value, path link value, and account link v…
turbolent Oct 31, 2023
72d7bfc
fix migration
turbolent Oct 31, 2023
f9bd178
bring back tests: manually create path capability values and link val…
turbolent Oct 31, 2023
b1a7613
ignore deprecation warnings, remove unused code
turbolent Oct 31, 2023
67140e3
rename
turbolent Oct 31, 2023
e81209b
generalize link creation
turbolent Oct 31, 2023
f96502b
report cyclic errors instead of panic-ing
turbolent Oct 31, 2023
44b8c4d
report missing link target
turbolent Oct 31, 2023
76b3ab0
use existing storage, do not create another one
turbolent Nov 7, 2023
c10c7b3
refactor tests
turbolent Nov 7, 2023
b2969af
test missing source
turbolent Nov 7, 2023
4d56a32
handle account links
turbolent Nov 8, 2023
16e249b
test private account link
turbolent Nov 8, 2023
08fb111
support migrated links, do not require storage target to exists
turbolent Nov 8, 2023
190b535
report and assert new borrow type
turbolent Nov 8, 2023
0ffefdc
do not mutate while iterating
turbolent Nov 8, 2023
6161d64
test migration for type mismatch between capability and link chain
turbolent Nov 8, 2023
54bb96a
test link migrations
turbolent Nov 9, 2023
4040bc5
lint
turbolent Nov 9, 2023
f4551a0
Merge branch 'feature/stable-cadence' into bastian/cap-cons-state-mig…
turbolent Nov 20, 2023
120a7f8
Merge branch 'supun/generalized-migration' into bastian/cap-cons-stat…
turbolent Dec 5, 2023
0aec1e7
move
turbolent Dec 5, 2023
601d5a2
use common address iterator
turbolent Dec 5, 2023
d0330d5
pass account address to value migration function
turbolent Dec 5, 2023
77ffa5f
simplify account type value migration
turbolent Dec 5, 2023
781e157
pass address path in one parameter, instead of three
turbolent Dec 6, 2023
83564de
split combined link and capability migration into two separate migrat…
turbolent Dec 6, 2023
b2e3e0f
Merge branch 'feature/stable-cadence' into bastian/cap-cons-state-mig…
turbolent Dec 6, 2023
6c9ef05
add new location range argument
turbolent Dec 6, 2023
92b13a4
fix lint
turbolent Dec 6, 2023
0292cfe
remove duplicated test case
turbolent Dec 8, 2023
3a0bff5
turn TODO comment into explanation
turbolent Dec 8, 2023
297682f
check if reporter exists
turbolent Dec 8, 2023
3784a40
improve comment
turbolent Dec 8, 2023
b3778eb
use new CompositeValue.ForEachFieldName and DictionaryValue.IterateKe…
turbolent Dec 8, 2023
676ad21
separate commit from migration, so multiple sequential migrations can…
turbolent Dec 8, 2023
bd1c39e
clean up, comment
turbolent Dec 8, 2023
d8c7c42
Merge branch 'feature/stable-cadence' into bastian/cap-cons-state-mig…
turbolent Dec 8, 2023
8b2ee05
Merge branch 'master' into bastian/cap-cons-state-migration
turbolent Dec 12, 2023
cec2925
fix lint
turbolent Dec 12, 2023
e46b8a9
simplify
turbolent Dec 12, 2023
e08a790
also handle path to account link chains
turbolent Dec 12, 2023
f568221
fix tests for re-added AuthAccount/PublicAccount primitive static types
turbolent Dec 12, 2023
2996575
Update migrations/capcons/linkmigration.go
turbolent Dec 15, 2023
b12ec56
Merge branch 'master' into bastian/cap-cons-state-migration
turbolent Dec 15, 2023
aee0846
Update encoding/ccf/simpletype_test.go
turbolent Dec 15, 2023
909282d
fix lint
turbolent Dec 19, 2023
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
24 changes: 17 additions & 7 deletions migrations/account_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,18 @@ func NewAccountStorage(storage *runtime.Storage, address common.Address) Account
}
}

type ValueConverter func(
addressPath interpreter.AddressPath,
value interpreter.Value,
) interpreter.Value

// ForEachValue iterates over the values in the account.
// The `valueConverter takes a function to be applied to each value.
// It returns the converted, if a new value was created during conversion.
func (i *AccountStorage) ForEachValue(
inter *interpreter.Interpreter,
domains []common.PathDomain,
valueConverter func(
value interpreter.Value,
address common.Address,
domain common.PathDomain,
key string,
) interpreter.Value,
valueConverter ValueConverter,
) {
for _, domain := range domains {
storageMap := i.storage.GetStorageMap(i.address, domain.Identifier(), false)
Expand All @@ -69,9 +69,19 @@ func (i *AccountStorage) ForEachValue(
for _, key := range keys {
storageKey := interpreter.StringStorageMapKey(key)

path := interpreter.PathValue{
Identifier: key,
Domain: domain,
}

addressPath := interpreter.AddressPath{
Address: i.address,
Path: path,
}

value := storageMap.ReadValue(nil, storageKey)

newValue := valueConverter(value, i.address, domain, key)
newValue := valueConverter(addressPath, value)
if newValue == nil {
continue
}
Expand Down
21 changes: 11 additions & 10 deletions migrations/account_type/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,26 @@ func (AccountTypeMigration) Name() string {

// Migrate migrates `AuthAccount` and `PublicAccount` types inside `TypeValue`s,
// to the account reference type (&Account).
func (AccountTypeMigration) Migrate(value interpreter.Value) (newValue interpreter.Value) {
func (AccountTypeMigration) Migrate(
_ interpreter.AddressPath,
value interpreter.Value,
_ *interpreter.Interpreter,
) interpreter.Value {
switch value := value.(type) {
case interpreter.TypeValue:
convertedType := maybeConvertAccountType(value.Type)
if convertedType == nil {
return
if convertedType != nil {
return interpreter.NewTypeValue(nil, convertedType)
}
return interpreter.NewTypeValue(nil, convertedType)

case *interpreter.CapabilityValue:
convertedBorrowType := maybeConvertAccountType(value.BorrowType)
if convertedBorrowType == nil {
return
if convertedBorrowType != nil {
return interpreter.NewUnmeteredCapabilityValue(value.ID, value.Address, convertedBorrowType)
}
return interpreter.NewUnmeteredCapabilityValue(value.ID, value.Address, convertedBorrowType)

default:
return nil
}

return nil
}

func maybeConvertAccountType(staticType interpreter.StaticType) interpreter.StaticType {
Expand Down
57 changes: 24 additions & 33 deletions migrations/account_type/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
package account_type

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/onflow/atree"
Expand All @@ -37,34 +37,20 @@ import (
var _ migrations.Reporter = &testReporter{}

type testReporter struct {
migratedPaths map[common.Address]map[common.PathDomain]map[string]struct{}
migratedPaths map[interpreter.AddressPath]struct{}
}

func newTestReporter() *testReporter {
return &testReporter{
migratedPaths: map[common.Address]map[common.PathDomain]map[string]struct{}{},
migratedPaths: map[interpreter.AddressPath]struct{}{},
}
}

func (t *testReporter) Report(
address common.Address,
domain common.PathDomain,
identifier string,
addressPath interpreter.AddressPath,
_ string,
) {
migratedPathsInAddress, ok := t.migratedPaths[address]
if !ok {
migratedPathsInAddress = make(map[common.PathDomain]map[string]struct{})
t.migratedPaths[address] = migratedPathsInAddress
}

migratedPathsInDomain, ok := migratedPathsInAddress[domain]
if !ok {
migratedPathsInDomain = make(map[string]struct{})
migratedPathsInAddress[domain] = migratedPathsInDomain
}

migratedPathsInDomain[identifier] = struct{}{}
t.migratedPaths[addressPath] = struct{}{}
}

func TestTypeValueMigration(t *testing.T) {
Expand Down Expand Up @@ -356,22 +342,23 @@ func TestTypeValueMigration(t *testing.T) {
NewAccountTypeMigration(),
)

// Check reported migrated paths

migratedPathsInDomain := reporter.migratedPaths[account][pathDomain]
for path, test := range testCases {
t.Run(fmt.Sprintf("reported_%s", path), func(t *testing.T) {
test := test
path := path
migration.Commit()

t.Parallel()
// Check reported migrated paths
for identifier, test := range testCases {
addressPath := interpreter.AddressPath{
Address: account,
Path: interpreter.PathValue{
Domain: pathDomain,
Identifier: identifier,
},
}

if test.expectedType == nil {
require.NotContains(t, migratedPathsInDomain, path)
} else {
require.Contains(t, migratedPathsInDomain, path)
}
})
if test.expectedType == nil {
assert.NotContains(t, reporter.migratedPaths, addressPath)
} else {
assert.Contains(t, reporter.migratedPaths, addressPath)
}
}

// Assert the migrated values.
Expand Down Expand Up @@ -665,6 +652,8 @@ func TestNestedTypeValueMigration(t *testing.T) {
NewAccountTypeMigration(),
)

migration.Commit()

// Assert: Traverse through the storage and see if the values are updated now.

storageMap := storage.GetStorageMap(account, pathDomain.Identifier(), false)
Expand Down Expand Up @@ -776,6 +765,8 @@ func TestValuesWithStaticTypeMigration(t *testing.T) {
NewAccountTypeMigration(),
)

migration.Commit()

// Assert: Traverse through the storage and see if the values are updated now.

storageMap := storage.GetStorageMap(account, pathDomain.Identifier(), false)
Expand Down
116 changes: 116 additions & 0 deletions migrations/capcons/capabilitymigration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* 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 capcons

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

type CapabilityMigrationReporter interface {
MigratedPathCapability(
accountAddress common.Address,
addressPath interpreter.AddressPath,
borrowType *interpreter.ReferenceStaticType,
)
MissingCapabilityID(
accountAddress common.Address,
addressPath interpreter.AddressPath,
)
}

// CapabilityMigration migrates all path capabilities to ID capabilities,
// using the path to ID capability controller mapping generated by LinkMigration.
type CapabilityMigration struct {
CapabilityIDs map[interpreter.AddressPath]interpreter.UInt64Value
Reporter CapabilityMigrationReporter
}

var _ migrations.Migration = &CapabilityMigration{}

func (*CapabilityMigration) Name() string {
return "CapabilityMigration"

Check warning on line 51 in migrations/capcons/capabilitymigration.go

View check run for this annotation

Codecov / codecov/patch

migrations/capcons/capabilitymigration.go#L50-L51

Added lines #L50 - L51 were not covered by tests
}

var fullyEntitledAccountReferenceStaticType = interpreter.ConvertSemaReferenceTypeToStaticReferenceType(
nil,
sema.FullyEntitledAccountReferenceType,
)

// Migrate migrates a path capability to an ID capability in the given value.
// If a value is returned, the value must be updated with the replacement in the parent.
// If nil is returned, the value was not updated and no operation has to be performed.
func (m *CapabilityMigration) Migrate(
addressPath interpreter.AddressPath,
value interpreter.Value,
_ *interpreter.Interpreter,
) interpreter.Value {
reporter := m.Reporter

switch value := value.(type) {
case *interpreter.PathCapabilityValue: //nolint:staticcheck

// Migrate the path capability to an ID capability

oldCapability := value

capabilityAddressPath := oldCapability.AddressPath()
capabilityID, ok := m.CapabilityIDs[capabilityAddressPath]
if !ok {
if reporter != nil {
reporter.MissingCapabilityID(
addressPath.Address,
capabilityAddressPath,
)
}
break
}

newBorrowType, ok := oldCapability.BorrowType.(*interpreter.ReferenceStaticType)
if !ok {
panic(errors.NewUnreachableError())

Check warning on line 90 in migrations/capcons/capabilitymigration.go

View check run for this annotation

Codecov / codecov/patch

migrations/capcons/capabilitymigration.go#L90

Added line #L90 was not covered by tests
}

// Convert the old AuthAccount type to the new fully-entitled Account type
if newBorrowType.ReferencedType == interpreter.PrimitiveStaticTypeAuthAccount { //nolint:staticcheck
newBorrowType = fullyEntitledAccountReferenceStaticType
}
SupunS marked this conversation as resolved.
Show resolved Hide resolved

newCapability := interpreter.NewUnmeteredCapabilityValue(
capabilityID,
oldCapability.Address,
newBorrowType,
)

if reporter != nil {
reporter.MigratedPathCapability(
addressPath.Address,
capabilityAddressPath,
newBorrowType,
)
}

return newCapability
}

return nil
}
55 changes: 55 additions & 0 deletions migrations/capcons/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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 capcons

import (
"fmt"
"strings"

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

// CyclicLinkError
type CyclicLinkError struct {
Paths []interpreter.PathValue
Address common.Address
}

var _ errors.UserError = CyclicLinkError{}

func (CyclicLinkError) IsUserError() {}

func (e CyclicLinkError) Error() string {
var builder strings.Builder
for i, path := range e.Paths {
if i > 0 {
builder.WriteString(" -> ")
}
builder.WriteString(path.String())

Check warning on line 46 in migrations/capcons/error.go

View check run for this annotation

Codecov / codecov/patch

migrations/capcons/error.go#L40-L46

Added lines #L40 - L46 were not covered by tests
}
paths := builder.String()

return fmt.Sprintf(
"cyclic link in account %s: %s",
e.Address.ShortHexWithPrefix(),
paths,
)

Check warning on line 54 in migrations/capcons/error.go

View check run for this annotation

Codecov / codecov/patch

migrations/capcons/error.go#L48-L54

Added lines #L48 - L54 were not covered by tests
}
Loading
Loading