Merge pull request #1989 from nspcc-dev/compiler-notify-name

compiler: check event name length in `runtime.Notify`
This commit is contained in:
Roman Khimov 2021-06-04 10:32:14 +03:00 committed by GitHub
commit ebff8be20a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 15 deletions

View file

@ -1,5 +1,5 @@
name: Test verify name: Test verify
Events: events:
- name: OnNEP11Payment - name: OnNEP11Payment
parameters: parameters:
- name: from - name: from

View file

@ -959,20 +959,6 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
ast.Walk(c, n.Fun) ast.Walk(c, n.Fun)
emit.Opcodes(c.prog.BinWriter, opcode.CALLA) emit.Opcodes(c.prog.BinWriter, opcode.CALLA)
case isSyscall(f): case isSyscall(f):
if f.pkg.Name() == "runtime" && f.name == "Notify" {
tv := c.typeAndValueOf(n.Args[0])
params := make([]string, 0, len(n.Args[1:]))
for _, p := range n.Args[1:] {
st, _ := c.scAndVMTypeFromExpr(p)
params = append(params, st.String())
}
// Sometimes event name is stored in a var.
// Skip in this case.
if tv.Value != nil {
name := constant.StringVal(tv.Value)
c.emittedEvents[name] = append(c.emittedEvents[name], params)
}
}
c.convertSyscall(f, n) c.convertSyscall(f, n)
default: default:
emit.Call(c.prog.BinWriter, opcode.CALLL, f.label) emit.Call(c.prog.BinWriter, opcode.CALLL, f.label)

View file

@ -9,6 +9,8 @@ import (
"github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -130,3 +132,46 @@ func TestOnPayableChecks(t *testing.T) {
require.Error(t, compileAndCheck(t, src)) require.Error(t, compileAndCheck(t, src))
}) })
} }
func TestEventWarnings(t *testing.T) {
src := `package payable
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func Main() { runtime.Notify("Event", 1) }`
_, di, err := compiler.CompileWithDebugInfo("eventTest", strings.NewReader(src))
require.NoError(t, err)
t.Run("event it missing from config", func(t *testing.T) {
_, err = compiler.CreateManifest(di, &compiler.Options{})
require.Error(t, err)
t.Run("suppress", func(t *testing.T) {
_, err = compiler.CreateManifest(di, &compiler.Options{NoEventsCheck: true})
require.NoError(t, err)
})
})
t.Run("wrong parameter number", func(t *testing.T) {
_, err = compiler.CreateManifest(di, &compiler.Options{
ContractEvents: []manifest.Event{{Name: "Event"}},
})
require.Error(t, err)
})
t.Run("wrong parameter type", func(t *testing.T) {
_, err = compiler.CreateManifest(di, &compiler.Options{
ContractEvents: []manifest.Event{{
Name: "Event",
Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.StringType)},
}},
})
require.Error(t, err)
})
t.Run("good", func(t *testing.T) {
_, err = compiler.CreateManifest(di, &compiler.Options{
ContractEvents: []manifest.Event{{
Name: "Event",
Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.IntegerType)},
}},
})
require.NoError(t, err)
})
}

View file

@ -1,9 +1,12 @@
package compiler package compiler
import ( import (
"fmt"
"go/ast" "go/ast"
"go/constant"
"go/types" "go/types"
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
) )
@ -27,6 +30,8 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
pkg := c.buildInfo.program.Package(f.pkg.Path()) pkg := c.buildInfo.program.Package(f.pkg.Path())
sig := c.typeOf(n.Fun).(*types.Signature) sig := c.typeOf(n.Fun).(*types.Signature)
c.processNotify(f, n.Args)
// When inlined call is used during global initialization // When inlined call is used during global initialization
// there is no func scope, thus this if. // there is no func scope, thus this if.
if c.scope == nil { if c.scope == nil {
@ -114,3 +119,28 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
c.importMap = oldMap c.importMap = oldMap
c.pkgInfoInline = c.pkgInfoInline[:len(c.pkgInfoInline)-1] c.pkgInfoInline = c.pkgInfoInline[:len(c.pkgInfoInline)-1]
} }
func (c *codegen) processNotify(f *funcScope, args []ast.Expr) {
if f != nil && f.pkg.Path() == interopPrefix+"/runtime" && f.name == "Notify" {
// Sometimes event name is stored in a var.
// Skip in this case.
tv := c.typeAndValueOf(args[0])
if tv.Value == nil {
return
}
params := make([]string, 0, len(args[1:]))
for _, p := range args[1:] {
st, _ := c.scAndVMTypeFromExpr(p)
params = append(params, st.String())
}
name := constant.StringVal(tv.Value)
if len(name) > runtime.MaxEventNameLen {
c.prog.Err = fmt.Errorf("event name '%s' should be less than %d",
name, runtime.MaxEventNameLen)
return
}
c.emittedEvents[name] = append(c.emittedEvents[name], params)
}
}

View file

@ -188,6 +188,17 @@ func TestNotify(t *testing.T) {
assert.Equal(t, exp0, s.events[0].Item.Value()) assert.Equal(t, exp0, s.events[0].Item.Value())
assert.Equal(t, "single", s.events[1].Name) assert.Equal(t, "single", s.events[1].Name)
assert.Equal(t, []stackitem.Item{}, s.events[1].Item.Value()) assert.Equal(t, []stackitem.Item{}, s.events[1].Item.Value())
t.Run("long event name", func(t *testing.T) {
src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func Main(arg int) {
runtime.Notify("long event12345678901234567890123")
}`
_, _, err := compiler.CompileWithDebugInfo("foo.go", strings.NewReader(src))
require.Error(t, err)
})
} }
func TestSyscallInGlobalInit(t *testing.T) { func TestSyscallInGlobalInit(t *testing.T) {