Skip to content

Commit

Permalink
Include EVM stdlib in staging update validator (#1697)
Browse files Browse the repository at this point in the history
  • Loading branch information
jribbink authored Aug 15, 2024
1 parent 819f8bc commit 5d187a5
Show file tree
Hide file tree
Showing 5 changed files with 501 additions and 311 deletions.
13 changes: 8 additions & 5 deletions internal/cadence/linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ func newLinter(state *flowkit.State) *linter {

// Create checker configs for both standard and script
// Scripts have a different stdlib than contracts and transactions
l.checkerStandardConfig = l.newCheckerConfig(util.NewStandardLibrary())
l.checkerScriptConfig = l.newCheckerConfig(util.NewScriptStandardLibrary())
l.checkerStandardConfig = l.newCheckerConfig(util.NewCheckerEnvironment())
l.checkerScriptConfig = l.newCheckerConfig(util.NewScriptCheckerEnvironment())

return l
}
Expand Down Expand Up @@ -154,10 +154,13 @@ func (l *linter) lintFile(
}

// Create a new checker config with the given standard library
func (l *linter) newCheckerConfig(lib util.StandardLibrary) *sema.Config {
func (l *linter) newCheckerConfig(env *util.CheckerEnvironment) *sema.Config {
return &sema.Config{
BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation {
return lib.BaseValueActivation
BaseValueActivationHandler: func(location common.Location) *sema.VariableActivation {
return env.GetBaseValueActivation(location)
},
BaseTypeActivationHandler: func(location common.Location) *sema.VariableActivation {
return env.GetBaseTypeActivation(location)
},
AccessCheckMode: sema.AccessCheckModeStrict,
PositionInfoEnabled: true, // Must be enabled for linters
Expand Down
19 changes: 17 additions & 2 deletions internal/migrate/staging_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ type stagingValidatorImpl struct {

// Cache for contract checkers which are reused during program checking & used for the update checker
checkingCache map[common.Location]*cachedCheckingResult

// Environment for the stdlib
env *util.CheckerEnvironment
}

type node map[common.Location]node
Expand Down Expand Up @@ -174,6 +177,14 @@ func newStagingValidator(flow flowkit.Services) *stagingValidatorImpl {
}

func (v *stagingValidatorImpl) Validate(stagedContracts []stagedContractUpdate) error {
// Setup the environment for the stdlib
chainId, ok := chainIdMap[v.flow.Network().Name]
if !ok {
return fmt.Errorf("unsupported network: %s", v.flow.Network().Name)
}
v.env = util.NewCheckerEnvironment()
v.env.SetupFVM(chainId)

v.stagedContracts = make(map[common.AddressLocation]stagedContractUpdate)
for _, stagedContract := range stagedContracts {
v.stagedContracts[stagedContract.DeployLocation] = stagedContract
Expand Down Expand Up @@ -408,9 +419,13 @@ func (v *stagingValidatorImpl) checkContract(
&sema.Config{
AccessCheckMode: sema.AccessCheckModeStrict,
AttachmentsEnabled: true,
BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation {
BaseValueActivationHandler: func(location common.Location) *sema.VariableActivation {
// Only checking contracts, so no need to consider script standard library
return v.env.GetBaseValueActivation(location)
},
BaseTypeActivationHandler: func(location common.Location) *sema.VariableActivation {
// Only checking contracts, so no need to consider script standard library
return util.NewStandardLibrary().BaseValueActivation
return v.env.GetBaseTypeActivation(location)
},
LocationHandler: v.resolveLocation,
ImportHandler: v.resolveImport,
Expand Down
60 changes: 60 additions & 0 deletions internal/migrate/staging_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -762,4 +762,64 @@ func Test_StagingValidator(t *testing.T) {

require.Nil(t, err)
})

t.Run("import InternalEVM fails with wrong location", func(t *testing.T) {
// setup mocks
srv := setupValidatorMocks(t, []mockNetworkAccount{
{
address: flow.HexToAddress("01"),
contracts: map[string][]byte{"Foo": []byte(`
pub contract Foo {
init() {}
}`)},
},
})

validator := newStagingValidator(srv)
err := validator.Validate([]stagedContractUpdate{
{
DeployLocation: simpleAddressLocation("0x01.Foo"),
SourceLocation: common.StringLocation("./Foo.cdc"),
Code: []byte(`
access(all) contract Foo {
init() {
log(InternalEVM)
}
}`),
},
})

var validatorErr *stagingValidatorError
require.ErrorAs(t, err, &validatorErr)

var checkerErr *sema.CheckerError
require.ErrorAs(t, validatorErr.errors[simpleAddressLocation("0x01.Foo")], &checkerErr)
require.ErrorContains(t, checkerErr, "cannot find variable in this scope: `InternalEVM`")
})

t.Run("EVM import works", func(t *testing.T) {
location := simpleAddressLocation("0x01.Test")
sourceCodeLocation := common.StringLocation("./Test.cdc")
oldContract := `
pub contract Test {
pub fun test() {}
}`
newContract := `
import EVM from 0x8c5303eaa26202d6
access(all) contract Test {
access(all) fun test() {}
}`

// setup mocks
srv := setupValidatorMocks(t, []mockNetworkAccount{
{
address: flow.HexToAddress("01"),
contracts: map[string][]byte{"Test": []byte(oldContract)},
},
})

validator := newStagingValidator(srv)
err := validator.Validate([]stagedContractUpdate{{location, sourceCodeLocation, []byte(newContract)}})
require.NoError(t, err)
})
}
Loading

0 comments on commit 5d187a5

Please sign in to comment.