Skip to content

Commit

Permalink
Merge pull request #3673 from onflow/fxamacker/refactor-storage-domains
Browse files Browse the repository at this point in the history
Refactor storage domains to prevent import cycles and simplify maintenance
  • Loading branch information
fxamacker authored Nov 12, 2024
2 parents 44376e4 + 2f8c298 commit 33c0ded
Show file tree
Hide file tree
Showing 23 changed files with 248 additions and 99 deletions.
2 changes: 1 addition & 1 deletion cmd/decode-state-values/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ type interpreterStorage struct {

var _ interpreter.Storage = &interpreterStorage{}

func (i interpreterStorage) GetStorageMap(_ common.Address, _ string, _ bool) *interpreter.StorageMap {
func (i interpreterStorage) GetStorageMap(_ common.Address, _ common.StorageDomain, _ bool) *interpreter.StorageMap {
panic("unexpected GetStorageMap call")
}

Expand Down
15 changes: 15 additions & 0 deletions common/pathdomain.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,18 @@ func (i PathDomain) Identifier() string {

panic(errors.NewUnreachableError())
}

func (i PathDomain) StorageDomain() StorageDomain {
switch i {
case PathDomainStorage:
return StorageDomainPathStorage

case PathDomainPrivate:
return StorageDomainPathPrivate

case PathDomainPublic:
return StorageDomainPathPublic
}

panic(errors.NewUnreachableError())
}
132 changes: 132 additions & 0 deletions common/storagedomain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* 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 common

import (
"fmt"

"github.com/onflow/cadence/errors"
)

type StorageDomain uint8

const (
StorageDomainUnknown StorageDomain = iota

StorageDomainPathStorage

StorageDomainPathPrivate

StorageDomainPathPublic

StorageDomainContract

StorageDomainInbox

// StorageDomainCapabilityController is the storage domain which stores
// capability controllers by capability ID
StorageDomainCapabilityController

// StorageDomainCapabilityControllerTag is the storage domain which stores
// capability controller tags by capability ID
StorageDomainCapabilityControllerTag

// StorageDomainPathCapability is the storage domain which stores
// capability ID dictionaries (sets) by storage path identifier
StorageDomainPathCapability

// StorageDomainAccountCapability is the storage domain which
// records active account capability controller IDs
StorageDomainAccountCapability
)

var AllStorageDomains = []StorageDomain{
StorageDomainPathStorage,
StorageDomainPathPrivate,
StorageDomainPathPublic,
StorageDomainContract,
StorageDomainInbox,
StorageDomainCapabilityController,
StorageDomainCapabilityControllerTag,
StorageDomainPathCapability,
StorageDomainAccountCapability,
}

var AllStorageDomainsByIdentifier = map[string]StorageDomain{}

var allStorageDomainsSet = map[StorageDomain]struct{}{}

func init() {
for _, domain := range AllStorageDomains {
identifier := domain.Identifier()
AllStorageDomainsByIdentifier[identifier] = domain

allStorageDomainsSet[domain] = struct{}{}
}
}

func StorageDomainFromIdentifier(domain string) (StorageDomain, bool) {
result, ok := AllStorageDomainsByIdentifier[domain]
if !ok {
return StorageDomainUnknown, false
}
return result, true
}

func StorageDomainFromUint64(i uint64) (StorageDomain, error) {
d := StorageDomain(i)
_, exists := allStorageDomainsSet[d]
if !exists {
return StorageDomainUnknown, fmt.Errorf("failed to convert %d to StorageDomain", i)
}
return d, nil
}

func (d StorageDomain) Identifier() string {
switch d {
case StorageDomainPathStorage:
return PathDomainStorage.Identifier()

case StorageDomainPathPrivate:
return PathDomainPrivate.Identifier()

case StorageDomainPathPublic:
return PathDomainPublic.Identifier()

case StorageDomainContract:
return "contract"

case StorageDomainInbox:
return "inbox"

case StorageDomainCapabilityController:
return "cap_con"

case StorageDomainCapabilityControllerTag:
return "cap_tag"

case StorageDomainPathCapability:
return "path_cap"

case StorageDomainAccountCapability:
return "acc_cap"
}

panic(errors.NewUnreachableError())
}
2 changes: 1 addition & 1 deletion interpreter/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ func testAccountWithErrorHandler(
}
storageKey := storageKey{
address: storageMapKey.Address,
domain: storageMapKey.Key,
domain: storageMapKey.Domain.Identifier(),
key: key,
}
accountValues[storageKey] = value
Expand Down
24 changes: 12 additions & 12 deletions interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ func (c TypeCodes) Merge(codes TypeCodes) {

type Storage interface {
atree.SlabStorage
GetStorageMap(address common.Address, domain string, createIfNotExists bool) *StorageMap
GetStorageMap(address common.Address, domain common.StorageDomain, createIfNotExists bool) *StorageMap
CheckHealth() error
}

Expand Down Expand Up @@ -967,7 +967,7 @@ func (interpreter *Interpreter) declareSelfVariable(value Value, locationRange L
}

func (interpreter *Interpreter) visitAssignment(
transferOperation ast.TransferOperation,
_ ast.TransferOperation,
targetGetterSetter getterSetter, targetType sema.Type,
valueExpression ast.Expression, valueType sema.Type,
position ast.HasPosition,
Expand Down Expand Up @@ -1271,7 +1271,7 @@ func (declarationInterpreter *Interpreter) declareNonEnumCompositeValue(
functions.Set(resourceDefaultDestroyEventName(compositeType), destroyEventConstructor)
}

applyDefaultFunctions := func(ty *sema.InterfaceType, code WrapperCode) {
applyDefaultFunctions := func(_ *sema.InterfaceType, code WrapperCode) {

// Apply default functions, if conforming type does not provide the function

Expand Down Expand Up @@ -2678,7 +2678,7 @@ func (interpreter *Interpreter) NewSubInterpreter(

func (interpreter *Interpreter) StoredValueExists(
storageAddress common.Address,
domain string,
domain common.StorageDomain,
identifier StorageMapKey,
) bool {
accountStorage := interpreter.Storage().GetStorageMap(storageAddress, domain, false)
Expand All @@ -2690,7 +2690,7 @@ func (interpreter *Interpreter) StoredValueExists(

func (interpreter *Interpreter) ReadStored(
storageAddress common.Address,
domain string,
domain common.StorageDomain,
identifier StorageMapKey,
) Value {
accountStorage := interpreter.Storage().GetStorageMap(storageAddress, domain, false)
Expand All @@ -2702,7 +2702,7 @@ func (interpreter *Interpreter) ReadStored(

func (interpreter *Interpreter) WriteStored(
storageAddress common.Address,
domain string,
domain common.StorageDomain,
key StorageMapKey,
value Value,
) (existed bool) {
Expand Down Expand Up @@ -4069,7 +4069,7 @@ func (interpreter *Interpreter) IsSubTypeOfSemaType(staticSubType StaticType, su
}

func (interpreter *Interpreter) domainPaths(address common.Address, domain common.PathDomain) []Value {
storageMap := interpreter.Storage().GetStorageMap(address, domain.Identifier(), false)
storageMap := interpreter.Storage().GetStorageMap(address, domain.StorageDomain(), false)
if storageMap == nil {
return []Value{}
}
Expand Down Expand Up @@ -4164,7 +4164,7 @@ func (interpreter *Interpreter) newStorageIterationFunction(
parameterTypes := fnType.ParameterTypes()
returnType := fnType.ReturnTypeAnnotation.Type

storageMap := config.Storage.GetStorageMap(address, domain.Identifier(), false)
storageMap := config.Storage.GetStorageMap(address, domain.StorageDomain(), false)
if storageMap == nil {
// if nothing is stored, no iteration is required
return Void
Expand Down Expand Up @@ -4327,7 +4327,7 @@ func (interpreter *Interpreter) authAccountSaveFunction(
panic(errors.NewUnreachableError())
}

domain := path.Domain.Identifier()
domain := path.Domain.StorageDomain()
identifier := path.Identifier

// Prevent an overwrite
Expand Down Expand Up @@ -4390,7 +4390,7 @@ func (interpreter *Interpreter) authAccountTypeFunction(
panic(errors.NewUnreachableError())
}

domain := path.Domain.Identifier()
domain := path.Domain.StorageDomain()
identifier := path.Identifier

storageMapKey := StringStorageMapKey(identifier)
Expand Down Expand Up @@ -4448,7 +4448,7 @@ func (interpreter *Interpreter) authAccountReadFunction(
panic(errors.NewUnreachableError())
}

domain := path.Domain.Identifier()
domain := path.Domain.StorageDomain()
identifier := path.Identifier

storageMapKey := StringStorageMapKey(identifier)
Expand Down Expand Up @@ -4589,7 +4589,7 @@ func (interpreter *Interpreter) authAccountCheckFunction(
panic(errors.NewUnreachableError())
}

domain := path.Domain.Identifier()
domain := path.Domain.StorageDomain()
identifier := path.Identifier

storageMapKey := StringStorageMapKey(identifier)
Expand Down
2 changes: 1 addition & 1 deletion interpreter/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5349,7 +5349,7 @@ func TestInterpretReferenceFailableDowncasting(t *testing.T) {
true, // r is standalone.
)

domain := storagePath.Domain.Identifier()
domain := storagePath.Domain.StorageDomain()
storageMap := storage.GetStorageMap(storageAddress, domain, true)
storageMapKey := interpreter.StringStorageMapKey(storagePath.Identifier)
storageMap.WriteValue(inter, storageMapKey, r)
Expand Down
25 changes: 21 additions & 4 deletions interpreter/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ func ConvertStoredValue(gauge common.MemoryGauge, value atree.Value) (Value, err
}
}

type StorageDomainKey struct {
Domain common.StorageDomain
Address common.Address
}

func NewStorageDomainKey(
memoryGauge common.MemoryGauge,
address common.Address,
domain common.StorageDomain,
) StorageDomainKey {
common.UseMemory(memoryGauge, common.StorageKeyMemoryUsage)
return StorageDomainKey{
Address: address,
Domain: domain,
}
}

type StorageKey struct {
Key string
Address common.Address
Expand Down Expand Up @@ -130,7 +147,7 @@ func (k StorageKey) IsLess(o StorageKey) bool {
// InMemoryStorage
type InMemoryStorage struct {
*atree.BasicSlabStorage
StorageMaps map[StorageKey]*StorageMap
StorageMaps map[StorageDomainKey]*StorageMap
memoryGauge common.MemoryGauge
}

Expand Down Expand Up @@ -158,19 +175,19 @@ func NewInMemoryStorage(memoryGauge common.MemoryGauge) InMemoryStorage {

return InMemoryStorage{
BasicSlabStorage: slabStorage,
StorageMaps: make(map[StorageKey]*StorageMap),
StorageMaps: make(map[StorageDomainKey]*StorageMap),
memoryGauge: memoryGauge,
}
}

func (i InMemoryStorage) GetStorageMap(
address common.Address,
domain string,
domain common.StorageDomain,
createIfNotExists bool,
) (
storageMap *StorageMap,
) {
key := NewStorageKey(i.memoryGauge, address, domain)
key := NewStorageDomainKey(i.memoryGauge, address, domain)
storageMap = i.StorageMaps[key]
if storageMap == nil && createIfNotExists {
storageMap = NewStorageMap(i.memoryGauge, i, atree.Address(address))
Expand Down
2 changes: 1 addition & 1 deletion interpreter/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ func TestStorageOverwriteAndRemove(t *testing.T) {

const storageMapKey = StringStorageMapKey("test")

storageMap := storage.GetStorageMap(address, "storage", true)
storageMap := storage.GetStorageMap(address, common.StorageDomainPathStorage, true)
storageMap.WriteValue(inter, storageMapKey, array1)

// Overwriting delete any existing child slabs
Expand Down
2 changes: 1 addition & 1 deletion interpreter/stringatreevalue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestLargeStringAtreeValueInSeparateSlab(t *testing.T) {

storageMap := storage.GetStorageMap(
common.MustBytesToAddress([]byte{0x1}),
common.PathDomainStorage.Identifier(),
common.PathDomainStorage.StorageDomain(),
true,
)

Expand Down
2 changes: 1 addition & 1 deletion interpreter/value_composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,7 @@ func (v *CompositeValue) getBaseValue(
return NewEphemeralReferenceValue(interpreter, functionAuthorization, v.base, baseType, locationRange)
}

func (v *CompositeValue) setBaseValue(interpreter *Interpreter, base *CompositeValue) {
func (v *CompositeValue) setBaseValue(_ *Interpreter, base *CompositeValue) {
v.base = base
}

Expand Down
2 changes: 1 addition & 1 deletion interpreter/value_storage_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (*StorageReferenceValue) IsImportable(_ *Interpreter, _ LocationRange) bool

func (v *StorageReferenceValue) dereference(interpreter *Interpreter, locationRange LocationRange) (*Value, error) {
address := v.TargetStorageAddress
domain := v.TargetPath.Domain.Identifier()
domain := v.TargetPath.Domain.StorageDomain()
identifier := v.TargetPath.Identifier

storageMapKey := StringStorageMapKey(identifier)
Expand Down
2 changes: 1 addition & 1 deletion interpreter/value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3806,7 +3806,7 @@ func TestValue_ConformsToStaticType(t *testing.T) {
)
require.NoError(t, err)

storageMap := storage.GetStorageMap(testAddress, "storage", true)
storageMap := storage.GetStorageMap(testAddress, common.StorageDomainPathStorage, true)
storageMap.WriteValue(inter, StringStorageMapKey("test"), TrueValue)

value := valueFactory(inter)
Expand Down
Loading

0 comments on commit 33c0ded

Please sign in to comment.