diff --git a/migrations/capcons/capabilities.go b/migrations/capcons/capabilities.go index f33f1e9bd..411c4f157 100644 --- a/migrations/capcons/capabilities.go +++ b/migrations/capcons/capabilities.go @@ -20,8 +20,11 @@ package capcons import ( "fmt" + "strings" "sync" + "golang.org/x/exp/slices" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/interpreter" ) @@ -60,6 +63,34 @@ func (c *AccountCapabilities) Record( ) } +func (c *AccountCapabilities) ForEach( + f func(AccountCapability) bool, +) { + slices.SortFunc( + c.Capabilities, + func(a, b AccountCapability) int { + pathA := a.TargetPath + pathB := b.TargetPath + + if pathA.Domain == pathB.Domain { + return strings.Compare(pathA.Identifier, pathB.Identifier) + } + + if pathA.Domain < pathB.Domain { + return -1 + } + + return +1 + }, + ) + + for _, accountCapability := range c.Capabilities { + if !f(accountCapability) { + return + } + } +} + type AccountsCapabilities struct { // accountCapabilities maps common.Address to *AccountCapabilities accountCapabilities sync.Map @@ -97,11 +128,8 @@ func (m *AccountsCapabilities) ForEach( } accountCapabilities := rawAccountCapabilities.(*AccountCapabilities) - for _, accountCapability := range accountCapabilities.Capabilities { - if !f(accountCapability) { - return - } - } + + accountCapabilities.ForEach(f) } func (m *AccountsCapabilities) Get(address common.Address) *AccountCapabilities { diff --git a/migrations/capcons/capabilities_test.go b/migrations/capcons/capabilities_test.go new file mode 100644 index 000000000..58634a87d --- /dev/null +++ b/migrations/capcons/capabilities_test.go @@ -0,0 +1,87 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Flow Foundation + * + * 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 ( + "testing" + + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/stretchr/testify/assert" +) + +func TestCapabilitiesIteration(t *testing.T) { + t.Parallel() + + caps := AccountCapabilities{} + + caps.Record( + interpreter.NewUnmeteredPathValue(common.PathDomainPublic, "b"), + nil, + interpreter.StorageKey{}, + nil, + ) + + caps.Record( + interpreter.NewUnmeteredPathValue(common.PathDomainPublic, "a"), + nil, + interpreter.StorageKey{}, + nil, + ) + + caps.Record( + interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "c"), + nil, + interpreter.StorageKey{}, + nil, + ) + + caps.Record( + interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "a"), + nil, + interpreter.StorageKey{}, + nil, + ) + + caps.Record( + interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "b"), + nil, + interpreter.StorageKey{}, + nil, + ) + + var paths []interpreter.PathValue + + caps.ForEach(func(capability AccountCapability) bool { + paths = append(paths, capability.TargetPath) + return true + }) + + assert.Equal( + t, + []interpreter.PathValue{ + interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "a"), + interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "b"), + interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "c"), + interpreter.NewUnmeteredPathValue(common.PathDomainPublic, "a"), + interpreter.NewUnmeteredPathValue(common.PathDomainPublic, "b"), + }, + paths, + ) +} diff --git a/migrations/capcons/storagecapmigration.go b/migrations/capcons/storagecapmigration.go index ba95c1dd1..2118ab7b5 100644 --- a/migrations/capcons/storagecapmigration.go +++ b/migrations/capcons/storagecapmigration.go @@ -109,7 +109,7 @@ func IssueAccountCapabilities( false, ) - for _, capability := range capabilities.Capabilities { + capabilities.ForEach(func(capability AccountCapability) bool { addressPath := interpreter.AddressPath{ Address: address, @@ -123,7 +123,7 @@ func IssueAccountCapabilities( if hasBorrowType { if _, ok := typedCapabilityMapping.Get(addressPath, capabilityBorrowType.ID()); ok { - continue + return true } borrowType = capabilityBorrowType.(*interpreter.ReferenceStaticType) @@ -141,7 +141,7 @@ func IssueAccountCapabilities( reporter.MissingBorrowType(addressPath, targetPath) if _, _, ok := untypedCapabilityMapping.Get(addressPath); ok { - continue + return true } // If the borrow type is missing, then borrow it as the type of the value. @@ -151,7 +151,7 @@ func IssueAccountCapabilities( // However, if there is no value at the target, //it is not possible to migrate this cap. if value == nil { - continue + return true } valueType := value.StaticType(inter) @@ -198,5 +198,7 @@ func IssueAccountCapabilities( borrowType, capabilityID, ) - } + + return true + }) }