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

Infer the missing borrow type for storage capabilities #6314

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
76 changes: 71 additions & 5 deletions cmd/util/ledger/migrations/cadence.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
import (
"context"
_ "embed"
"fmt"

Check failure on line 6 in cmd/util/ledger/migrations/cadence.go

View workflow job for this annotation

GitHub Actions / Lint (./)

File is not `goimports`-ed with -local github.com/onflow/flow-go (goimports)

"github.com/rs/zerolog"

"github.com/onflow/cadence/migrations/capcons"
"github.com/onflow/cadence/migrations/statictypes"
"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/flow-go/cmd/util/ledger/reporters"
"github.com/onflow/flow-go/cmd/util/ledger/util"
Expand Down Expand Up @@ -235,6 +236,8 @@
log zerolog.Logger
}

var _ AccountBasedMigration = &IssueStorageCapConMigration{}

const issueStorageCapConMigrationReporterName = "cadence-storage-capcon-issue-migration"

func NewIssueStorageCapConMigration(
Expand Down Expand Up @@ -328,13 +331,12 @@
m.verboseErrorOutput,
)

capcons.IssueAccountCapabilities(
migrationRuntime.Interpreter,
m.issueAccountCapabilities(
migrationRuntime,
reporter,
address,
accountCapabilities,
handler,
m.mapping,
)

return nil
Expand All @@ -345,7 +347,71 @@
return nil
}

var _ AccountBasedMigration = &IssueStorageCapConMigration{}
func (m *IssueStorageCapConMigration) issueAccountCapabilities(
Copy link
Member Author

@SupunS SupunS Aug 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method was copied over and modified from Cadence side. Need to access the storage, etc. so having it here makes more sense.
The old method can be removed from cadence (tech-debt).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to re-incorporate the logic from onflow/cadence#3516 once it's merged, or move the changes here to Cadence.

migrationRuntime *InterpreterMigrationRuntime,
reporter capcons.StorageCapabilityMigrationReporter,
address common.Address,
capabilities *capcons.AccountCapabilities,
handler stdlib.CapabilityControllerIssueHandler,
) {

storageMap := migrationRuntime.Storage.GetStorageMap(
address,
common.PathDomainStorage.Identifier(),
false,
)

inter := migrationRuntime.Interpreter

for _, capability := range capabilities.Capabilities {
addressPath := interpreter.AddressPath{
Address: address,
Path: capability.Path,
}

borrowStaticType := capability.BorrowType
path := capability.Path.Identifier

if borrowStaticType == nil {
reporter.MissingBorrowType(address, addressPath)

// If the borrow type is missing, treat it as `AnyStruct` or `AnyResource`.
// To determine whether it is a resource, read the target path.
Comment on lines +378 to +379
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The capability has direct access to the storage path, without any borrow type "restriction", so theoretically the capability could be borrowed with any type. Inferring to &AnyStruct / &AnyResource (or even &Any) might not be sufficient, for two reasons:

  1. The capability could be borrowed with entitlements, but the inferred type does not grant any.

    For example, imagine a capability targeting /storage/flowTokenVault, not having a borrow type, and getting borrowed with auth(FungibleToken.Withdraw) &FlowToken.Vault.

  2. The value stored under the path could change (e.g. even from a resource to a struct or vice-versa), making the inferred borrow type "too restrictive"

The first issue is more problematic than the latter.

Maybe we should just infer a more specific type, based on the stored value at the time of migration. This would make 2) worse, but would at least fix 1)

value := storageMap.ReadValue(nil, interpreter.StringStorageMapKey(path))

assumedBorrowType := interpreter.PrimitiveStaticTypeAnyStruct
if value.IsResourceKinded(inter) {
assumedBorrowType = interpreter.PrimitiveStaticTypeAnyResource
}

borrowStaticType = interpreter.NewReferenceStaticType(
nil,
interpreter.UnauthorizedAccess,
assumedBorrowType,
)
}

borrowType := inter.MustConvertStaticToSemaType(borrowStaticType).(*sema.ReferenceType)

capabilityID, _ := stdlib.IssueStorageCapabilityController(
inter,
interpreter.EmptyLocationRange,
handler,
address,
borrowType,
capability.Path,
)

m.mapping.Record(addressPath, capabilityID, borrowType)

reporter.IssuedStorageCapabilityController(
address,
addressPath,
borrowStaticType.(*interpreter.ReferenceStaticType),
capabilityID,
)
}
}

func NewCadence1ValueMigrations(
log zerolog.Logger,
Expand Down
53 changes: 45 additions & 8 deletions cmd/util/ledger/migrations/cadence_values_migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2169,7 +2169,20 @@ func TestCapabilityMigration(t *testing.T) {
storage := runtime.Storage
storageDomain := common.PathDomainStorage.Identifier()

storageMap := storage.GetStorageMap(
storageMapForAddressB := storage.GetStorageMap(
addressB,
storageDomain,
true,
)

// First store the target value.
storageMapForAddressB.WriteValue(
runtime.Interpreter,
interpreter.StringStorageMapKey("bar"),
interpreter.NewUnmeteredStringValue("This is the bar value"),
)

storageMapForAddressA := storage.GetStorageMap(
addressA,
storageDomain,
true,
Expand All @@ -2194,7 +2207,7 @@ func TestCapabilityMigration(t *testing.T) {
Address: interpreter.AddressValue(addressB),
}

storageMap.WriteValue(
storageMapForAddressA.WriteValue(
runtime.Interpreter,
fooCapStorageMapKey,
capabilityFoo,
Expand All @@ -2212,7 +2225,8 @@ func TestCapabilityMigration(t *testing.T) {
Address: interpreter.AddressValue(addressB),
}

storageMap.WriteValue(
// Then store the capability value.
storageMapForAddressA.WriteValue(
runtime.Interpreter,
barCapStorageMapKey,
capabilityBar,
Expand Down Expand Up @@ -2272,17 +2286,27 @@ func TestCapabilityMigration(t *testing.T) {

reporter := rwf.reportWriters[capabilityValueMigrationReporterName]
require.NotNil(t, reporter)
require.Len(t, reporter.entries, 3)
require.Len(t, reporter.entries, 4)

require.Equal(
t,
[]any{
capabilityMissingCapabilityIDEntry{
capabilityMigrationEntry{
AccountAddress: addressA,
AddressPath: interpreter.AddressPath{
Address: addressB,
Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "bar"),
},
BorrowType: borrowType,
CapabilityID: 3,
},
cadenceValueMigrationEntry{
StorageKey: interpreter.StorageKey{
Key: storageDomain,
Address: addressA,
},
StorageMapKey: barCapStorageMapKey,
Migration: "CapabilityValueMigration",
},
capabilityMigrationEntry{
AccountAddress: addressA,
Expand All @@ -2291,7 +2315,7 @@ func TestCapabilityMigration(t *testing.T) {
Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "foo"),
},
BorrowType: borrowType,
CapabilityID: 3,
CapabilityID: 4,
},
cadenceValueMigrationEntry{
StorageKey: interpreter.StorageKey{
Expand All @@ -2307,7 +2331,7 @@ func TestCapabilityMigration(t *testing.T) {

issueStorageCapConReporter := rwf.reportWriters[issueStorageCapConMigrationReporterName]
require.NotNil(t, issueStorageCapConReporter)
require.Len(t, issueStorageCapConReporter.entries, 2)
require.Len(t, issueStorageCapConReporter.entries, 3)
require.Equal(
t,
[]any{
Expand All @@ -2318,14 +2342,27 @@ func TestCapabilityMigration(t *testing.T) {
Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "bar"),
},
},
storageCapConIssuedEntry{
AccountAddress: addressB,
AddressPath: interpreter.AddressPath{
Address: addressB,
Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "bar"),
},
BorrowType: interpreter.NewReferenceStaticType(
nil,
interpreter.UnauthorizedAccess,
interpreter.PrimitiveStaticTypeAnyStruct,
),
CapabilityID: 3,
},
storageCapConIssuedEntry{
AccountAddress: addressB,
AddressPath: interpreter.AddressPath{
Address: addressB,
Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "foo"),
},
BorrowType: borrowType,
CapabilityID: 3,
CapabilityID: 4,
},
},
issueStorageCapConReporter.entries,
Expand Down
Loading