compiler: disallow runtime.Notify
in Verify
function
Only direct invocations are considered. The check can be disabled with '--no-events' compiler option.
This commit is contained in:
parent
ebff8be20a
commit
1578904da2
4 changed files with 53 additions and 2 deletions
|
@ -337,6 +337,12 @@ func isInitFunc(decl *ast.FuncDecl) bool {
|
|||
decl.Type.Results.NumFields() == 0
|
||||
}
|
||||
|
||||
func (c *codegen) isVerifyFunc(decl *ast.FuncDecl) bool {
|
||||
return decl.Name.Name == "Verify" && decl.Recv == nil &&
|
||||
decl.Type.Results.NumFields() == 1 &&
|
||||
isBool(c.typeOf(decl.Type.Results.List[0].Type))
|
||||
}
|
||||
|
||||
func (c *codegen) clearSlots(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
emit.Opcodes(c.prog.BinWriter, opcode.PUSHNULL)
|
||||
|
|
|
@ -59,6 +59,7 @@ type Options struct {
|
|||
type buildInfo struct {
|
||||
initialPackage string
|
||||
program *loader.Program
|
||||
options *Options
|
||||
}
|
||||
|
||||
// ForEachPackage executes fn on each package used in the current program
|
||||
|
@ -153,10 +154,18 @@ func Compile(name string, r io.Reader) ([]byte, error) {
|
|||
|
||||
// CompileWithDebugInfo compiles a Go program into bytecode and emits debug info.
|
||||
func CompileWithDebugInfo(name string, r io.Reader) ([]byte, *DebugInfo, error) {
|
||||
return CompileWithOptions(name, r, &Options{
|
||||
NoEventsCheck: true,
|
||||
})
|
||||
}
|
||||
|
||||
// CompileWithOptions compiles a Go program into bytecode with provided compiler options.
|
||||
func CompileWithOptions(name string, r io.Reader, o *Options) ([]byte, *DebugInfo, error) {
|
||||
ctx, err := getBuildInfo(name, r)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ctx.options = o
|
||||
return CodeGen(ctx)
|
||||
}
|
||||
|
||||
|
@ -173,7 +182,7 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
|
|||
if len(o.Ext) == 0 {
|
||||
o.Ext = fileExt
|
||||
}
|
||||
b, di, err := CompileWithDebugInfo(src, nil)
|
||||
b, di, err := CompileWithOptions(src, nil, o)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while trying to compile smart contract file: %w", err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package compiler_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -175,3 +176,24 @@ func TestEventWarnings(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNotifyInVerify(t *testing.T) {
|
||||
srcTmpl := `package payable
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
func Verify() bool { runtime.%s("Event"); return true }`
|
||||
|
||||
for _, name := range []string{"Notify", "Log"} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
src := fmt.Sprintf(srcTmpl, name)
|
||||
_, _, err := compiler.CompileWithOptions("eventTest", strings.NewReader(src),
|
||||
&compiler.Options{ContractEvents: []manifest.Event{{Name: "Event"}}})
|
||||
require.Error(t, err)
|
||||
|
||||
t.Run("suppress", func(t *testing.T) {
|
||||
_, _, err := compiler.CompileWithOptions("eventTest", strings.NewReader(src),
|
||||
&compiler.Options{NoEventsCheck: true})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,7 +121,21 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
|
|||
}
|
||||
|
||||
func (c *codegen) processNotify(f *funcScope, args []ast.Expr) {
|
||||
if f != nil && f.pkg.Path() == interopPrefix+"/runtime" && f.name == "Notify" {
|
||||
if f != nil && f.pkg.Path() == interopPrefix+"/runtime" {
|
||||
if f.name != "Notify" && f.name != "Log" {
|
||||
return
|
||||
}
|
||||
|
||||
if c.scope != nil && c.isVerifyFunc(c.scope.decl) &&
|
||||
c.scope.pkg == c.mainPkg.Pkg && !c.buildInfo.options.NoEventsCheck {
|
||||
c.prog.Err = fmt.Errorf("runtime.%s is not allowed in `Verify`", f.name)
|
||||
return
|
||||
}
|
||||
|
||||
if f.name == "Log" {
|
||||
return
|
||||
}
|
||||
|
||||
// Sometimes event name is stored in a var.
|
||||
// Skip in this case.
|
||||
tv := c.typeAndValueOf(args[0])
|
||||
|
|
Loading…
Reference in a new issue