Skip to content

Commit

Permalink
Add FT/NFT type change rules for contract update validator
Browse files Browse the repository at this point in the history
  • Loading branch information
SupunS committed Feb 20, 2024
1 parent 4d7d75d commit c86ba7e
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 26 deletions.
23 changes: 15 additions & 8 deletions cmd/util/ledger/migrations/cadence.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,35 @@ import (
"github.com/onflow/flow-go/model/flow"
)

func NewCadence1InterfaceStaticTypeConverter(chainID flow.ChainID) statictypes.InterfaceTypeConverterFunc {
func NewInterfaceTypeConversionRules(chainID flow.ChainID) StaticTypeMigrationRules {
systemContracts := systemcontracts.SystemContractsForChain(chainID)

oldFungibleTokenResolverType, newFungibleTokenResolverType := fungibleTokenResolverRule(systemContracts)

rules := StaticTypeMigrationRules{
return StaticTypeMigrationRules{
oldFungibleTokenResolverType.ID(): newFungibleTokenResolverType,
}

return NewStaticTypeMigrator[*interpreter.InterfaceStaticType](rules)
}

func NewCadence1CompositeStaticTypeConverter(chainID flow.ChainID) statictypes.CompositeTypeConverterFunc {

func NewCompositeTypeConversionRules(chainID flow.ChainID) StaticTypeMigrationRules {
systemContracts := systemcontracts.SystemContractsForChain(chainID)

oldFungibleTokenVaultCompositeType, newFungibleTokenVaultType := fungibleTokenVaultRule(systemContracts)
oldNonFungibleTokenNFTCompositeType, newNonFungibleTokenNFTType := nonFungibleTokenNFTRule(systemContracts)

rules := StaticTypeMigrationRules{
return StaticTypeMigrationRules{
oldFungibleTokenVaultCompositeType.ID(): newFungibleTokenVaultType,
oldNonFungibleTokenNFTCompositeType.ID(): newNonFungibleTokenNFTType,
}
}

func NewCadence1InterfaceStaticTypeConverter(chainID flow.ChainID) statictypes.InterfaceTypeConverterFunc {
rules := NewInterfaceTypeConversionRules(chainID)
return NewStaticTypeMigrator[*interpreter.InterfaceStaticType](rules)
}

func NewCadence1CompositeStaticTypeConverter(chainID flow.ChainID) statictypes.CompositeTypeConverterFunc {
rules := NewCompositeTypeConversionRules(chainID)
return NewStaticTypeMigrator[*interpreter.CompositeStaticType](rules)
}

Expand Down Expand Up @@ -193,7 +198,9 @@ func NewCadence1ContractsMigrations(
stagedContracts []StagedContract,
) []ledger.Migration {

stagedContractsMigration := NewStagedContractsMigration(chainID)
stagedContractsMigration := NewStagedContractsMigration(chainID).
WithContractUpdateValidation()

stagedContractsMigration.RegisterContractUpdates(stagedContracts)

return []ledger.Migration{
Expand Down
49 changes: 40 additions & 9 deletions cmd/util/ledger/migrations/staged_contracts_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ import (
)

type StagedContractsMigration struct {
name string
chainID flow.ChainID
log zerolog.Logger
mutex sync.RWMutex
stagedContracts map[common.Address]map[flow.RegisterID]Contract
contractsByLocation map[common.Location][]byte
enableUpdateValidation bool
name string
chainID flow.ChainID
log zerolog.Logger
mutex sync.RWMutex
stagedContracts map[common.Address]map[flow.RegisterID]Contract
contractsByLocation map[common.Location][]byte
enableUpdateValidation bool
userDefinedTypeChangeCheckFunc func(oldTypeID common.TypeID, newTypeID common.TypeID) (checked bool, valid bool)
}

type StagedContract struct {
Expand All @@ -56,6 +57,7 @@ func NewStagedContractsMigration(chainID flow.ChainID) *StagedContractsMigration

func (m *StagedContractsMigration) WithContractUpdateValidation() *StagedContractsMigration {
m.enableUpdateValidation = true
m.userDefinedTypeChangeCheckFunc = newUserDefinedTypeChangeCheckerFunc(m.chainID)
return m
}

Expand Down Expand Up @@ -212,7 +214,7 @@ func (m *StagedContractsMigration) MigrateAccount(
oldCode := payload.Value()

if m.enableUpdateValidation {
err = CheckContractUpdateValidity(
err = m.checkContractUpdateValidity(
mr,
address,
name,
Expand Down Expand Up @@ -254,7 +256,7 @@ func (m *StagedContractsMigration) MigrateAccount(
return payloads, nil
}

func CheckContractUpdateValidity(
func (m *StagedContractsMigration) checkContractUpdateValidity(
mr *migratorRuntime,
address common.Address,
contractName string,
Expand Down Expand Up @@ -292,5 +294,34 @@ func CheckContractUpdateValidity(
elaborations,
)

validator.WithUserDefinedTypeChangeChecker(
m.userDefinedTypeChangeCheckFunc,
)

return validator.Validate()
}

func newUserDefinedTypeChangeCheckerFunc(
chainID flow.ChainID,
) func(oldTypeID common.TypeID, newTypeID common.TypeID) (checked, valid bool) {

typeChangeRules := map[common.TypeID]common.TypeID{}

compositeTypeRules := NewCompositeTypeConversionRules(chainID)
for typeID, newStaticType := range compositeTypeRules {
typeChangeRules[typeID] = newStaticType.ID()
}

interfaceTypeRules := NewInterfaceTypeConversionRules(chainID)
for typeID, newStaticType := range interfaceTypeRules {
typeChangeRules[typeID] = newStaticType.ID()
}

return func(oldTypeID common.TypeID, newTypeID common.TypeID) (checked, valid bool) {
expectedNewTypeID, found := typeChangeRules[oldTypeID]
if found {
return true, expectedNewTypeID == newTypeID
}
return false, false
}
}
160 changes: 160 additions & 0 deletions cmd/util/ledger/migrations/staged_contracts_migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
"io"
"testing"

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

"github.com/rs/zerolog"

"github.com/onflow/flow-go/fvm/systemcontracts"
"github.com/onflow/flow-go/ledger"
"github.com/onflow/flow-go/ledger/common/convert"
"github.com/onflow/flow-go/model/flow"
Expand Down Expand Up @@ -573,3 +575,161 @@ func TestStagedContractsWithImports(t *testing.T) {
require.Equal(t, newCodeC, string(payloads[2].Value()))
})
}

func TestStagedContractsWithUpdateValidator(t *testing.T) {
t.Parallel()

chainID := flow.Emulator
systemContracts := systemcontracts.SystemContractsForChain(chainID)

address, err := common.HexToAddress("0x1")
require.NoError(t, err)

ctx := context.Background()

t.Run("FungibleToken.Vault", func(t *testing.T) {
t.Parallel()

ftAddress := common.Address(systemContracts.FungibleToken.Address)

oldCodeA := fmt.Sprintf(`
import FungibleToken from %s
pub contract A {
pub var vault: @FungibleToken.Vault?
init() {
self.vault <- nil
}
}
`,
ftAddress.HexWithPrefix(),
)

newCodeA := fmt.Sprintf(`
import FungibleToken from %s
access(all) contract A {
access(all) var vault: @{FungibleToken.Vault}?
init() {
self.vault <- nil
}
}
`,
ftAddress.HexWithPrefix(),
)

ftContract := `
access(all) contract FungibleToken {
access(all) resource interface Vault {}
}
`

stagedContracts := []StagedContract{
{
Contract: Contract{
Name: "A",
Code: []byte(newCodeA),
},
Address: address,
},
}

migration := NewStagedContractsMigration(chainID)
migration.RegisterContractUpdates(stagedContracts)
migration.WithContractUpdateValidation()

logWriter := &logWriter{}
log := zerolog.New(logWriter)
err = migration.InitMigration(log, nil, 0)
require.NoError(t, err)

payloads := []*ledger.Payload{
newContractPayload(address, "A", []byte(oldCodeA)),
newContractPayload(ftAddress, "FungibleToken", []byte(ftContract)),
}

payloads, err = migration.MigrateAccount(ctx, address, payloads)
require.NoError(t, err)

err = migration.Close()
require.NoError(t, err)

require.Empty(t, logWriter.logs)

require.Len(t, payloads, 2)
require.Equal(t, newCodeA, string(payloads[0].Value()))
})

t.Run("other type", func(t *testing.T) {
t.Parallel()

otherAddress, err := common.HexToAddress("0x2")
require.NoError(t, err)

oldCodeA := fmt.Sprintf(`
import FungibleToken from %s
pub contract A {
pub var vault: @FungibleToken.Vault?
init() {
self.vault <- nil
}
}
`,
otherAddress.HexWithPrefix(), // Importing from some other address
)

newCodeA := fmt.Sprintf(`
import FungibleToken from %s
access(all) contract A {
access(all) var vault: @{FungibleToken.Vault}?
init() {
self.vault <- nil
}
}
`,
otherAddress.HexWithPrefix(), // Importing from some other address
)

ftContract := `
access(all) contract FungibleToken {
access(all) resource interface Vault {}
}
`

stagedContracts := []StagedContract{
{
Contract: Contract{
Name: "A",
Code: []byte(newCodeA),
},
Address: address,
},
}

migration := NewStagedContractsMigration(chainID)
migration.RegisterContractUpdates(stagedContracts)
migration.WithContractUpdateValidation()

logWriter := &logWriter{}
log := zerolog.New(logWriter)
err = migration.InitMigration(log, nil, 0)
require.NoError(t, err)

payloads := []*ledger.Payload{
newContractPayload(address, "A", []byte(oldCodeA)),
newContractPayload(otherAddress, "FungibleToken", []byte(ftContract)),
}

payloads, err = migration.MigrateAccount(ctx, address, payloads)
require.NoError(t, err)

err = migration.Close()
require.NoError(t, err)

require.Len(t, logWriter.logs, 1)
assert.Contains(t, logWriter.logs[0], "cannot update contract `A`")

require.Len(t, payloads, 2)
require.Equal(t, oldCodeA, string(payloads[0].Value()))
})
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ require (
github.com/multiformats/go-multiaddr-dns v0.3.1
github.com/multiformats/go-multihash v0.2.3
github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f
github.com/onflow/cadence v1.0.0-preview.2.0.20240216110422-a67dbbf21171
github.com/onflow/cadence v1.0.0-preview.2.0.20240220162541-acde2e928710
github.com/onflow/crypto v0.25.0
github.com/onflow/flow v0.3.4
github.com/onflow/flow-core-contracts/lib/go/contracts v0.15.2-0.20240206003101-928bf99024d7
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2476,8 +2476,8 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6
github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f h1:Z8/PgTqOgOg02MTRpTBYO2k16FE6z4wEOtaC2WBR9Xo=
github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM=
github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8=
github.com/onflow/cadence v1.0.0-preview.2.0.20240216110422-a67dbbf21171 h1:l9AQBie6xFp3+vv+pWfUctSR6Uax7j5RZpAhP2z06So=
github.com/onflow/cadence v1.0.0-preview.2.0.20240216110422-a67dbbf21171/go.mod h1:a4mccDU90hmuxCLUFzs9J/ANG/rYbFa36h4Z0bBAqNU=
github.com/onflow/cadence v1.0.0-preview.2.0.20240220162541-acde2e928710 h1:NQ/8SnXikjdKNmLLITGo9RVhV71m+RwjhCmOAkvPKio=
github.com/onflow/cadence v1.0.0-preview.2.0.20240220162541-acde2e928710/go.mod h1:a4mccDU90hmuxCLUFzs9J/ANG/rYbFa36h4Z0bBAqNU=
github.com/onflow/crypto v0.25.0 h1:BeWbLsh3ZD13Ej+Uky6kg1PL1ZIVBDVX+2MVBNwqddg=
github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI=
github.com/onflow/flow v0.3.4 h1:FXUWVdYB90f/rjNcY0Owo30gL790tiYff9Pb/sycXYE=
Expand Down
2 changes: 1 addition & 1 deletion insecure/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ require (
github.com/multiformats/go-varint v0.0.7 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f // indirect
github.com/onflow/cadence v1.0.0-preview.2.0.20240216110422-a67dbbf21171 // indirect
github.com/onflow/cadence v1.0.0-preview.2.0.20240220162541-acde2e928710 // indirect
github.com/onflow/flow-core-contracts/lib/go/contracts v0.15.2-0.20240206003101-928bf99024d7 // indirect
github.com/onflow/flow-core-contracts/lib/go/templates v0.15.2-0.20240206003101-928bf99024d7 // indirect
github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20240205224107-320aa3cf09e0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions insecure/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2463,8 +2463,8 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6
github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f h1:Z8/PgTqOgOg02MTRpTBYO2k16FE6z4wEOtaC2WBR9Xo=
github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM=
github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8=
github.com/onflow/cadence v1.0.0-preview.2.0.20240216110422-a67dbbf21171 h1:l9AQBie6xFp3+vv+pWfUctSR6Uax7j5RZpAhP2z06So=
github.com/onflow/cadence v1.0.0-preview.2.0.20240216110422-a67dbbf21171/go.mod h1:a4mccDU90hmuxCLUFzs9J/ANG/rYbFa36h4Z0bBAqNU=
github.com/onflow/cadence v1.0.0-preview.2.0.20240220162541-acde2e928710 h1:NQ/8SnXikjdKNmLLITGo9RVhV71m+RwjhCmOAkvPKio=
github.com/onflow/cadence v1.0.0-preview.2.0.20240220162541-acde2e928710/go.mod h1:a4mccDU90hmuxCLUFzs9J/ANG/rYbFa36h4Z0bBAqNU=
github.com/onflow/crypto v0.25.0 h1:BeWbLsh3ZD13Ej+Uky6kg1PL1ZIVBDVX+2MVBNwqddg=
github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI=
github.com/onflow/flow-core-contracts/lib/go/contracts v0.15.2-0.20240206003101-928bf99024d7 h1:OI/4F2NK/X/4x3dTUFFDGtuOsSa9pX+jjBeSEcBrY/M=
Expand Down
2 changes: 1 addition & 1 deletion integration/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/ipfs/go-datastore v0.6.0
github.com/ipfs/go-ds-badger2 v0.1.3
github.com/ipfs/go-ipfs-blockstore v1.3.0
github.com/onflow/cadence v1.0.0-preview.2.0.20240216110422-a67dbbf21171
github.com/onflow/cadence v1.0.0-preview.2.0.20240220162541-acde2e928710
github.com/onflow/crypto v0.25.0
github.com/onflow/flow-core-contracts/lib/go/contracts v0.15.2-0.20240206003101-928bf99024d7
github.com/onflow/flow-core-contracts/lib/go/templates v0.15.2-0.20240206003101-928bf99024d7
Expand Down
4 changes: 2 additions & 2 deletions integration/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2538,8 +2538,8 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6
github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f h1:Z8/PgTqOgOg02MTRpTBYO2k16FE6z4wEOtaC2WBR9Xo=
github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM=
github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8=
github.com/onflow/cadence v1.0.0-preview.2.0.20240216110422-a67dbbf21171 h1:l9AQBie6xFp3+vv+pWfUctSR6Uax7j5RZpAhP2z06So=
github.com/onflow/cadence v1.0.0-preview.2.0.20240216110422-a67dbbf21171/go.mod h1:a4mccDU90hmuxCLUFzs9J/ANG/rYbFa36h4Z0bBAqNU=
github.com/onflow/cadence v1.0.0-preview.2.0.20240220162541-acde2e928710 h1:NQ/8SnXikjdKNmLLITGo9RVhV71m+RwjhCmOAkvPKio=
github.com/onflow/cadence v1.0.0-preview.2.0.20240220162541-acde2e928710/go.mod h1:a4mccDU90hmuxCLUFzs9J/ANG/rYbFa36h4Z0bBAqNU=
github.com/onflow/crypto v0.25.0 h1:BeWbLsh3ZD13Ej+Uky6kg1PL1ZIVBDVX+2MVBNwqddg=
github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI=
github.com/onflow/flow-core-contracts/lib/go/contracts v0.15.2-0.20240206003101-928bf99024d7 h1:OI/4F2NK/X/4x3dTUFFDGtuOsSa9pX+jjBeSEcBrY/M=
Expand Down

0 comments on commit c86ba7e

Please sign in to comment.