Merge pull request #1995 from nspcc-dev/compiler-verify-notify
compiler: disallow `runtime.Notify` in `Verify` function
This commit is contained in:
commit
efb814a97e
4 changed files with 53 additions and 2 deletions
|
@ -353,6 +353,12 @@ func isInitFunc(decl *ast.FuncDecl) bool {
|
||||||
decl.Type.Results.NumFields() == 0
|
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) {
|
func (c *codegen) clearSlots(n int) {
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
emit.Opcodes(c.prog.BinWriter, opcode.PUSHNULL)
|
emit.Opcodes(c.prog.BinWriter, opcode.PUSHNULL)
|
||||||
|
|
|
@ -59,6 +59,7 @@ type Options struct {
|
||||||
type buildInfo struct {
|
type buildInfo struct {
|
||||||
initialPackage string
|
initialPackage string
|
||||||
program *loader.Program
|
program *loader.Program
|
||||||
|
options *Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForEachPackage executes fn on each package used in the current program
|
// 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.
|
// CompileWithDebugInfo compiles a Go program into bytecode and emits debug info.
|
||||||
func CompileWithDebugInfo(name string, r io.Reader) ([]byte, *DebugInfo, error) {
|
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)
|
ctx, err := getBuildInfo(name, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
ctx.options = o
|
||||||
return CodeGen(ctx)
|
return CodeGen(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +182,7 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
|
||||||
if len(o.Ext) == 0 {
|
if len(o.Ext) == 0 {
|
||||||
o.Ext = fileExt
|
o.Ext = fileExt
|
||||||
}
|
}
|
||||||
b, di, err := CompileWithDebugInfo(src, nil)
|
b, di, err := CompileWithOptions(src, nil, o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error while trying to compile smart contract file: %w", err)
|
return nil, fmt.Errorf("error while trying to compile smart contract file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package compiler_test
|
package compiler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
@ -175,3 +176,24 @@ func TestEventWarnings(t *testing.T) {
|
||||||
require.NoError(t, err)
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -110,7 +110,21 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) processNotify(f *funcScope, args []ast.Expr) {
|
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.
|
// Sometimes event name is stored in a var.
|
||||||
// Skip in this case.
|
// Skip in this case.
|
||||||
tv := c.typeAndValueOf(args[0])
|
tv := c.typeAndValueOf(args[0])
|
||||||
|
|
Loading…
Reference in a new issue