From 299a7ea614054db584196eb80f1bd1fb3468da66 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 8 Dec 2021 22:33:03 +0300 Subject: [PATCH] compiler: use CALLT for native contract calls, fix #2257 --- internal/testchain/transaction.go | 10 +-- pkg/compiler/analysis.go | 2 +- pkg/compiler/codegen.go | 98 +++++++++++++++++++-- pkg/compiler/compiler.go | 32 +++---- pkg/compiler/function_call_test.go | 4 +- pkg/compiler/global_test.go | 2 +- pkg/compiler/inline.go | 17 ++-- pkg/compiler/interop_test.go | 59 ++++++++++--- pkg/compiler/native_test.go | 61 +++++++------ pkg/compiler/syscall_test.go | 2 +- pkg/compiler/vm_test.go | 2 +- pkg/core/helper_test.go | 6 +- pkg/interop/native/crypto/crypto.go | 7 +- pkg/interop/native/gas/gas.go | 13 +-- pkg/interop/native/ledger/ledger.go | 13 +-- pkg/interop/native/management/management.go | 25 +++--- pkg/interop/native/neo/neo.go | 37 ++++---- pkg/interop/native/notary/notary.go | 13 +-- pkg/interop/native/oracle/oracle.go | 10 +-- pkg/interop/native/policy/policy.go | 19 ++-- pkg/interop/native/roles/roles.go | 9 +- pkg/interop/native/std/std.go | 42 ++++----- pkg/interop/neogointernal/call.go | 12 +++ pkg/neotest/compile.go | 10 +-- pkg/vm/cli/cli.go | 2 +- pkg/vm/cli/cli_test.go | 5 +- 26 files changed, 322 insertions(+), 190 deletions(-) create mode 100644 pkg/interop/neogointernal/call.go diff --git a/internal/testchain/transaction.go b/internal/testchain/transaction.go index 7059affc3..5797bee08 100644 --- a/internal/testchain/transaction.go +++ b/internal/testchain/transaction.go @@ -17,7 +17,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -56,12 +55,7 @@ func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160, // nef.NewFile() cares about version a lot. config.Version = "0.90.0-test" - avm, di, err := compiler.CompileWithDebugInfo(name, r) - if err != nil { - return nil, util.Uint160{}, nil, err - } - - ne, err := nef.NewFile(avm) + ne, di, err := compiler.CompileWithDebugInfo(name, r) if err != nil { return nil, util.Uint160{}, nil, err } @@ -108,7 +102,7 @@ func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160, tx.Signers = []transaction.Signer{{Account: sender}} h := state.CreateContractHash(tx.Sender(), ne.Checksum, name) - return tx, h, avm, nil + return tx, h, ne.Script, nil } // SignTx signs provided transactions with validator keys. diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index d39549d6f..b1ecc288b 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -353,7 +353,7 @@ func isSyscall(fun *funcScope) bool { return false } return fun.pkg.Name() == "neogointernal" && (strings.HasPrefix(fun.name, "Syscall") || - strings.HasPrefix(fun.name, "Opcode")) + strings.HasPrefix(fun.name, "Opcode") || strings.HasPrefix(fun.name, "CallWithToken")) } const interopPrefix = "github.com/nspcc-dev/neo-go/pkg/interop" diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index e028b48d4..91c464775 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -15,6 +15,8 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util/bitfield" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -115,6 +117,9 @@ type codegen struct { // Label table for recording jump destinations. l []int + + // Tokens for CALLT instruction + callTokens []nef.MethodToken } type labelOffsetType byte @@ -1569,17 +1574,86 @@ func getJumpForToken(tok token.Token, typ types.Type) (opcode.Opcode, bool) { return 0, false } +func (c *codegen) getCallToken(hash util.Uint160, method string, pcount int, hasReturn bool, flag callflag.CallFlag) (uint16, error) { + needed := nef.MethodToken{ + Hash: hash, + Method: method, + ParamCount: uint16(pcount), + HasReturn: hasReturn, + CallFlag: flag, + } + for i := range c.callTokens { + if c.callTokens[i] == needed { + return uint16(i), nil + } + } + if len(c.callTokens) == math.MaxUint16 { + return 0, errors.New("call token overflow") + } + c.callTokens = append(c.callTokens, needed) + return uint16(len(c.callTokens) - 1), nil +} + func (c *codegen) convertSyscall(f *funcScope, expr *ast.CallExpr) { - for _, arg := range expr.Args[1:] { + var callArgs = expr.Args[1:] + + if strings.HasPrefix(f.name, "CallWithToken") { + callArgs = expr.Args[3:] + } + for _, arg := range callArgs { ast.Walk(c, arg) } tv := c.typeAndValueOf(expr.Args[0]) - name := constant.StringVal(tv.Value) + if tv.Value == nil || !isString(tv.Type) { + c.prog.Err = fmt.Errorf("bad intrinsic argument") + return + } + arg0Str := constant.StringVal(tv.Value) + if strings.HasPrefix(f.name, "Syscall") { - c.emitReverse(len(expr.Args) - 1) - emit.Syscall(c.prog.BinWriter, name) + c.emitReverse(len(callArgs)) + emit.Syscall(c.prog.BinWriter, arg0Str) + } else if strings.HasPrefix(f.name, "CallWithToken") { + var hasRet = !strings.HasSuffix(f.name, "NoRet") + + c.emitReverse(len(callArgs)) + + hash, err := util.Uint160DecodeBytesBE([]byte(arg0Str)) + if err != nil { + c.prog.Err = fmt.Errorf("bad callt hash: %w", err) + return + } + + tv = c.typeAndValueOf(expr.Args[1]) + if tv.Value == nil || !isString(tv.Type) { + c.prog.Err = errors.New("bad callt method") + return + } + method := constant.StringVal(tv.Value) + + tv = c.typeAndValueOf(expr.Args[2]) + if tv.Value == nil || !isNumber(tv.Type) { + c.prog.Err = errors.New("bad callt call flags") + return + } + flag, ok := constant.Uint64Val(tv.Value) + if !ok || flag > 255 { + c.prog.Err = errors.New("invalid callt flag") + return + } + + c.appendInvokedContract(hash, method, flag) + + tokNum, err := c.getCallToken(hash, method, len(callArgs), hasRet, callflag.CallFlag(flag)) + if err != nil { + c.prog.Err = err + return + } + tokBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(tokBuf, tokNum) + emit.Instruction(c.prog.BinWriter, opcode.CALLT, tokBuf) } else { - op, err := opcode.FromString(name) + op, err := opcode.FromString(arg0Str) if err != nil { c.prog.Err = fmt.Errorf("invalid opcode: %s", op) return @@ -2058,8 +2132,8 @@ func newCodegen(info *buildInfo, pkg *loader.PackageInfo) *codegen { } } -// CodeGen compiles the program to bytecode. -func CodeGen(info *buildInfo) ([]byte, *DebugInfo, error) { +// codeGen compiles the program to bytecode. +func codeGen(info *buildInfo) (*nef.File, *DebugInfo, error) { pkg := info.program.Package(info.initialPackage) c := newCodegen(info, pkg) @@ -2077,7 +2151,15 @@ func CodeGen(info *buildInfo) ([]byte, *DebugInfo, error) { for i := range di.Methods { methods.Set(int(di.Methods[i].Range.Start)) } - return buf, di, vm.IsScriptCorrect(buf, methods) + f, err := nef.NewFile(buf) + if err != nil { + return nil, nil, fmt.Errorf("error while trying to create .nef file: %w", err) + } + if c.callTokens != nil { + f.Tokens = c.callTokens + } + f.Checksum = f.CalculateChecksum() + return f, di, vm.IsScriptCorrect(buf, methods) } func (c *codegen) resolveFuncDecls(f *ast.File, pkg *types.Package) { diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 296782dd4..988f26825 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -160,29 +160,29 @@ func getBuildInfo(name string, src interface{}) (*buildInfo, error) { // If `r != nil`, `name` is interpreted as a filename, and `r` as file contents. // Otherwise `name` is either file name or name of the directory containing source files. func Compile(name string, r io.Reader) ([]byte, error) { - buf, _, err := CompileWithDebugInfo(name, r) + f, _, err := CompileWithDebugInfo(name, r) if err != nil { return nil, err } - return buf, nil + return f.Script, nil } // 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) (*nef.File, *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) { +func CompileWithOptions(name string, r io.Reader, o *Options) (*nef.File, *DebugInfo, error) { ctx, err := getBuildInfo(name, r) if err != nil { return nil, nil, err } ctx.options = o - return CodeGen(ctx) + return codeGen(ctx) } // CompileAndSave will compile and save the file to disk in the NEF format. @@ -198,14 +198,10 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { if len(o.Ext) == 0 { o.Ext = fileExt } - b, di, err := CompileWithOptions(src, nil, o) + f, di, err := CompileWithOptions(src, nil, o) if err != nil { return nil, fmt.Errorf("error while trying to compile smart contract file: %w", err) } - f, err := nef.NewFile(b) - if err != nil { - return nil, fmt.Errorf("error while trying to create .nef file: %w", err) - } if o.SourceURL != "" { if len(o.SourceURL) > nef.MaxSourceURLLength { return nil, errors.New("too long source URL") @@ -220,10 +216,10 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { out := fmt.Sprintf("%s.%s", o.Outfile, o.Ext) err = ioutil.WriteFile(out, bytes, os.ModePerm) if err != nil { - return b, err + return f.Script, err } if o.DebugInfo == "" && o.ManifestFile == "" { - return b, nil + return f.Script, nil } if o.DebugInfo != "" { @@ -246,26 +242,26 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { } data, err := json.Marshal(di) if err != nil { - return b, err + return f.Script, err } if err := ioutil.WriteFile(o.DebugInfo, data, os.ModePerm); err != nil { - return b, err + return f.Script, err } } if o.ManifestFile != "" { m, err := CreateManifest(di, o) if err != nil { - return b, err + return f.Script, err } mData, err := json.Marshal(m) if err != nil { - return b, fmt.Errorf("failed to marshal manifest to JSON: %w", err) + return f.Script, fmt.Errorf("failed to marshal manifest to JSON: %w", err) } - return b, ioutil.WriteFile(o.ManifestFile, mData, os.ModePerm) + return f.Script, ioutil.WriteFile(o.ManifestFile, mData, os.ModePerm) } - return b, nil + return f.Script, nil } // CreateManifest creates manifest and checks that is is valid. diff --git a/pkg/compiler/function_call_test.go b/pkg/compiler/function_call_test.go index 9503f59ba..6fed34dfd 100644 --- a/pkg/compiler/function_call_test.go +++ b/pkg/compiler/function_call_test.go @@ -308,9 +308,9 @@ func TestJumpOptimize(t *testing.T) { for _, mi := range di.Methods { // only _deploy and init have locals here if mi.Name.Name == "_deploy" || mi.Name.Name == "init" { - require.Equal(t, b[mi.Range.Start], byte(opcode.INITSLOT)) + require.Equal(t, b.Script[mi.Range.Start], byte(opcode.INITSLOT)) } - require.Equal(t, b[mi.Range.End], byte(opcode.RET)) + require.Equal(t, b.Script[mi.Range.End], byte(opcode.RET)) } } diff --git a/pkg/compiler/global_test.go b/pkg/compiler/global_test.go index f58673d07..d0176c7ec 100644 --- a/pkg/compiler/global_test.go +++ b/pkg/compiler/global_test.go @@ -134,7 +134,7 @@ func TestContractWithNoMain(t *testing.T) { b, di, err := compiler.CompileWithDebugInfo("foo.go", strings.NewReader(src)) require.NoError(t, err) v := vm.New() - invokeMethod(t, "Add3", b, v, di) + invokeMethod(t, "Add3", b.Script, v, di) v.Estack().PushVal(39) require.NoError(t, v.Run()) require.Equal(t, 1, v.Estack().Len()) diff --git a/pkg/compiler/inline.go b/pkg/compiler/inline.go index 702ef46e2..63eb25ea7 100644 --- a/pkg/compiler/inline.go +++ b/pkg/compiler/inline.go @@ -189,12 +189,6 @@ func (c *codegen) processContractCall(f *funcScope, args []ast.Expr) { } method := constant.StringVal(value) - currLst := c.invokedContracts[u] - for _, m := range currLst { - if m == method { - return - } - } value = c.typeAndValueOf(args[2]).Value if value == nil { @@ -202,6 +196,17 @@ func (c *codegen) processContractCall(f *funcScope, args []ast.Expr) { } flag, _ := constant.Uint64Val(value) + c.appendInvokedContract(u, method, flag) +} + +func (c *codegen) appendInvokedContract(u util.Uint160, method string, flag uint64) { + currLst := c.invokedContracts[u] + for _, m := range currLst { + if m == method { + return + } + } + if flag&uint64(callflag.WriteStates|callflag.AllowNotify) != 0 { c.invokedContracts[u] = append(currLst, method) } diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 89624f06d..f2d2447d7 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -19,7 +19,6 @@ import ( cinterop "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -132,8 +131,8 @@ func spawnVM(t *testing.T, ic *interop.Context, src string) *vm.VM { b, di, err := compiler.CompileWithDebugInfo("foo.go", strings.NewReader(src)) require.NoError(t, err) v := core.SpawnVM(ic) - invokeMethod(t, testMainIdent, b, v, di) - v.LoadScriptWithFlags(b, callflag.All) + invokeMethod(t, testMainIdent, b.Script, v, di) + v.LoadScriptWithFlags(b.Script, callflag.All) return v } @@ -147,7 +146,7 @@ func TestAppCall(t *testing.T) { mBar, err := di.ConvertToManifest(&compiler.Options{Name: "Bar"}) require.NoError(t, err) - barH := hash.Hash160(barCtr) + barH := hash.Hash160(barCtr.Script) srcInner := `package foo import "github.com/nspcc-dev/neo-go/pkg/interop/contract" @@ -178,25 +177,21 @@ func TestAppCall(t *testing.T) { }) require.NoError(t, err) - ih := hash.Hash160(inner) + ih := hash.Hash160(inner.Script) var contractGetter = func(_ dao.DAO, h util.Uint160) (*state.Contract, error) { if h.Equals(ih) { - innerNef, err := nef.NewFile(inner) - require.NoError(t, err) return &state.Contract{ ContractBase: state.ContractBase{ Hash: ih, - NEF: *innerNef, + NEF: *inner, Manifest: *m, }, }, nil } else if h.Equals(barH) { - barNef, err := nef.NewFile(barCtr) - require.NoError(t, err) return &state.Contract{ ContractBase: state.ContractBase{ Hash: barH, - NEF: *barNef, + NEF: *barCtr, Manifest: *mBar, }, }, nil @@ -382,3 +377,45 @@ func TestLenForNil(t *testing.T) { eval(t, src, true) } + +func TestCallTConversionErrors(t *testing.T) { + t.Run("variable hash", func(t *testing.T) { + src := `package foo + import "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" + func Main() int { + var hash string + return neogointernal.CallWithToken(hash, "method", 0).(int) + }` + _, err := compiler.Compile("foo.go", strings.NewReader(src)) + require.Error(t, err) + }) + t.Run("bad hash", func(t *testing.T) { + src := `package foo + import "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" + func Main() int { + return neogointernal.CallWithToken("badstring", "method", 0).(int) + }` + _, err := compiler.Compile("foo.go", strings.NewReader(src)) + require.Error(t, err) + }) + t.Run("variable method", func(t *testing.T) { + src := `package foo + import "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" + func Main() int { + var method string + return neogointernal.CallWithToken("\xf5\x63\xea\x40\xbc\x28\x3d\x4d\x0e\x05\xc4\x8e\xa3\x05\xb3\xf2\xa0\x73\x40\xef", method, 0).(int) + }` + _, err := compiler.Compile("foo.go", strings.NewReader(src)) + require.Error(t, err) + }) + t.Run("variable flags", func(t *testing.T) { + src := `package foo + import "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" + func Main() { + var flags int + neogointernal.CallWithTokenNoRet("\xf5\x63\xea\x40\xbc\x28\x3d\x4d\x0e\x05\xc4\x8e\xa3\x05\xb3\xf2\xa0\x73\x40\xef", "method", flags) + }` + _, err := compiler.Compile("foo.go", strings.NewReader(src)) + require.Error(t, err) + }) +} diff --git a/pkg/compiler/native_test.go b/pkg/compiler/native_test.go index e2a733b6a..ecb9317ba 100644 --- a/pkg/compiler/native_test.go +++ b/pkg/compiler/native_test.go @@ -1,14 +1,15 @@ package compiler_test import ( + "errors" "fmt" "math/big" "strings" "testing" + "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core/transaction" @@ -23,8 +24,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop/native/roles" "github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/smartcontract" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" - "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/require" @@ -250,10 +249,41 @@ func runNativeTestCase(t *testing.T, ctr interop.ContractMD, name, method string methodUpper = strings.ReplaceAll(methodUpper, "Json", "JSON") src := fmt.Sprintf(srcTmpl, name, name, methodUpper, strings.Join(params, ",")) - v, s := vmAndCompileInterop(t, src) - id := interopnames.ToID([]byte(interopnames.SystemContractCall)) + v := vm.New() + v.GasLimit = -1 + b, di, err := compiler.CompileWithDebugInfo("foo.go", strings.NewReader(src)) + require.NoError(t, err) + result := getTestStackItem(md.MD.ReturnType) - s.interops[id] = testContractCall(t, ctr.Hash, md, result) + v.LoadToken = func(id int32) error { + t := b.Tokens[id] + if t.Hash != ctr.Hash { + return fmt.Errorf("wrong hash %s", t.Hash.StringLE()) + } + if t.Method != md.MD.Name { + return fmt.Errorf("wrong name %s", t.Method) + } + if int(t.ParamCount) != len(md.MD.Parameters) { + return fmt.Errorf("wrong number of parameters %v", t.ParamCount) + } + if t.HasReturn != !isVoid { + return fmt.Errorf("wrong hasReturn %v", t.HasReturn) + } + if t.CallFlag != md.RequiredFlags { + return fmt.Errorf("wrong flags %v", t.CallFlag) + } + for i := 0; i < int(t.ParamCount); i++ { + _ = v.Estack().Pop() + } + if v.Estack().Len() != 0 { + return errors.New("excessive parameters on the stack") + } + if !isVoid { + v.Estack().PushVal(result) + } + return nil + } + invokeMethod(t, testMainIdent, b.Script, v, di) require.NoError(t, v.Run()) if isVoid { require.Equal(t, 0, v.Estack().Len()) @@ -287,22 +317,3 @@ func getTestStackItem(typ smartcontract.ParamType) stackitem.Item { panic("unexpected type") } } - -func testContractCall(t *testing.T, hash util.Uint160, md interop.MethodAndPrice, result stackitem.Item) func(*vm.VM) error { - return func(v *vm.VM) error { - h := v.Estack().Pop().Bytes() - require.Equal(t, hash.BytesBE(), h) - - method := v.Estack().Pop().String() - require.Equal(t, md.MD.Name, method) - - fs := callflag.CallFlag(int32(v.Estack().Pop().BigInt().Int64())) - require.Equal(t, fs, md.RequiredFlags) - - args := v.Estack().Pop().Array() - require.Equal(t, len(md.MD.Parameters), len(args)) - - v.Estack().PushVal(result) - return nil - } -} diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index 2d864c520..f2b71821f 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -143,7 +143,7 @@ func runSyscallTestCase(t *testing.T, ic *interop.Context, goName string, tc sys require.NoError(t, err) v := ic.SpawnVM() - v.LoadScriptWithFlags(b, callflag.All) + v.LoadScriptWithFlags(b.Script, callflag.All) require.NoError(t, v.Run()) require.True(t, called) if tc.isVoid { diff --git a/pkg/compiler/vm_test.go b/pkg/compiler/vm_test.go index 73eed9e48..174ac5963 100644 --- a/pkg/compiler/vm_test.go +++ b/pkg/compiler/vm_test.go @@ -75,7 +75,7 @@ func vmAndCompileInterop(t *testing.T, src string) (*vm.VM, *storagePlugin) { require.NoError(t, err) storePlugin.info = di - invokeMethod(t, testMainIdent, b, vm, di) + invokeMethod(t, testMainIdent, b.Script, vm, di) return vm, storePlugin } diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index c76c8b531..f117c9825 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -33,7 +33,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -166,13 +165,10 @@ func TestBug1728(t *testing.T) { func _deploy(_ interface{}, isUpdate bool) { runtime.Log("Deploy") }` - b, di, err := compiler.CompileWithDebugInfo("foo", strings.NewReader(src)) + nf, di, err := compiler.CompileWithDebugInfo("foo", strings.NewReader(src)) require.NoError(t, err) m, err := di.ConvertToManifest(&compiler.Options{Name: "TestContract"}) require.NoError(t, err) - nf, err := nef.NewFile(b) - require.NoError(t, err) - nf.CalculateChecksum() rawManifest, err := json.Marshal(m) require.NoError(t, err) diff --git a/pkg/interop/native/crypto/crypto.go b/pkg/interop/native/crypto/crypto.go index 1a2c8709f..8bf8acf6c 100644 --- a/pkg/interop/native/crypto/crypto.go +++ b/pkg/interop/native/crypto/crypto.go @@ -7,6 +7,7 @@ package crypto import ( "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) // Hash represents CryptoLib contract hash. @@ -23,16 +24,16 @@ const ( // Sha256 calls `sha256` method of native CryptoLib contract and computes SHA256 hash of b. func Sha256(b []byte) interop.Hash256 { - return contract.Call(interop.Hash160(Hash), "sha256", contract.NoneFlag, b).(interop.Hash256) + return neogointernal.CallWithToken(Hash, "sha256", int(contract.NoneFlag), b).(interop.Hash256) } // Ripemd160 calls `ripemd160` method of native CryptoLib contract and computes RIPEMD160 hash of b. func Ripemd160(b []byte) interop.Hash160 { - return contract.Call(interop.Hash160(Hash), "ripemd160", contract.NoneFlag, b).(interop.Hash160) + return neogointernal.CallWithToken(Hash, "ripemd160", int(contract.NoneFlag), b).(interop.Hash160) } // VerifyWithECDsa calls `verifyWithECDsa` method of native CryptoLib contract and checks that sig is // correct msg's signature for a given pub (serialized public key on a given curve). func VerifyWithECDsa(msg []byte, pub interop.PublicKey, sig interop.Signature, curve NamedCurve) bool { - return contract.Call(interop.Hash160(Hash), "verifyWithECDsa", contract.NoneFlag, msg, pub, sig, curve).(bool) + return neogointernal.CallWithToken(Hash, "verifyWithECDsa", int(contract.NoneFlag), msg, pub, sig, curve).(bool) } diff --git a/pkg/interop/native/gas/gas.go b/pkg/interop/native/gas/gas.go index 1d0cc9329..538ac0a36 100644 --- a/pkg/interop/native/gas/gas.go +++ b/pkg/interop/native/gas/gas.go @@ -7,6 +7,7 @@ package gas import ( "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) // Hash represents GAS contract hash. @@ -14,26 +15,26 @@ const Hash = "\xcf\x76\xe2\x8b\xd0\x06\x2c\x4a\x47\x8e\xe3\x55\x61\x01\x13\x19\x // Symbol represents `symbol` method of GAS native contract. func Symbol() string { - return contract.Call(interop.Hash160(Hash), "symbol", contract.NoneFlag).(string) + return neogointernal.CallWithToken(Hash, "symbol", int(contract.NoneFlag)).(string) } // Decimals represents `decimals` method of GAS native contract. func Decimals() int { - return contract.Call(interop.Hash160(Hash), "decimals", contract.NoneFlag).(int) + return neogointernal.CallWithToken(Hash, "decimals", int(contract.NoneFlag)).(int) } // TotalSupply represents `totalSupply` method of GAS native contract. func TotalSupply() int { - return contract.Call(interop.Hash160(Hash), "totalSupply", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "totalSupply", int(contract.ReadStates)).(int) } // BalanceOf represents `balanceOf` method of GAS native contract. func BalanceOf(addr interop.Hash160) int { - return contract.Call(interop.Hash160(Hash), "balanceOf", contract.ReadStates, addr).(int) + return neogointernal.CallWithToken(Hash, "balanceOf", int(contract.ReadStates), addr).(int) } // Transfer represents `transfer` method of GAS native contract. func Transfer(from, to interop.Hash160, amount int, data interface{}) bool { - return contract.Call(interop.Hash160(Hash), "transfer", - contract.All, from, to, amount, data).(bool) + return neogointernal.CallWithToken(Hash, "transfer", + int(contract.All), from, to, amount, data).(bool) } diff --git a/pkg/interop/native/ledger/ledger.go b/pkg/interop/native/ledger/ledger.go index 028eb6e59..8de5deb4b 100644 --- a/pkg/interop/native/ledger/ledger.go +++ b/pkg/interop/native/ledger/ledger.go @@ -7,6 +7,7 @@ package ledger import ( "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) // Hash represents Ledger contract hash. @@ -14,31 +15,31 @@ const Hash = "\xbe\xf2\x04\x31\x40\x36\x2a\x77\xc1\x50\x99\xc7\xe6\x4c\x12\xf7\x // CurrentHash represents `currentHash` method of Ledger native contract. func CurrentHash() interop.Hash256 { - return contract.Call(interop.Hash160(Hash), "currentHash", contract.ReadStates).(interop.Hash256) + return neogointernal.CallWithToken(Hash, "currentHash", int(contract.ReadStates)).(interop.Hash256) } // CurrentIndex represents `currentIndex` method of Ledger native contract. func CurrentIndex() int { - return contract.Call(interop.Hash160(Hash), "currentIndex", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "currentIndex", int(contract.ReadStates)).(int) } // GetBlock represents `getBlock` method of Ledger native contract. func GetBlock(indexOrHash interface{}) *Block { - return contract.Call(interop.Hash160(Hash), "getBlock", contract.ReadStates, indexOrHash).(*Block) + return neogointernal.CallWithToken(Hash, "getBlock", int(contract.ReadStates), indexOrHash).(*Block) } // GetTransaction represents `getTransaction` method of Ledger native contract. func GetTransaction(hash interop.Hash256) *Transaction { - return contract.Call(interop.Hash160(Hash), "getTransaction", contract.ReadStates, hash).(*Transaction) + return neogointernal.CallWithToken(Hash, "getTransaction", int(contract.ReadStates), hash).(*Transaction) } // GetTransactionHeight represents `getTransactionHeight` method of Ledger native contract. func GetTransactionHeight(hash interop.Hash256) int { - return contract.Call(interop.Hash160(Hash), "getTransactionHeight", contract.ReadStates, hash).(int) + return neogointernal.CallWithToken(Hash, "getTransactionHeight", int(contract.ReadStates), hash).(int) } // GetTransactionFromBlock represents `getTransactionFromBlock` method of Ledger native contract. func GetTransactionFromBlock(indexOrHash interface{}, txIndex int) *Transaction { - return contract.Call(interop.Hash160(Hash), "getTransactionFromBlock", contract.ReadStates, + return neogointernal.CallWithToken(Hash, "getTransactionFromBlock", int(contract.ReadStates), indexOrHash, txIndex).(*Transaction) } diff --git a/pkg/interop/native/management/management.go b/pkg/interop/native/management/management.go index 97c109391..617e153fd 100644 --- a/pkg/interop/native/management/management.go +++ b/pkg/interop/native/management/management.go @@ -7,6 +7,7 @@ package management import ( "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) // Hash represents Management contract hash. @@ -14,44 +15,44 @@ const Hash = "\xfd\xa3\xfa\x43\x46\xea\x53\x2a\x25\x8f\xc4\x97\xdd\xad\xdb\x64\x // Deploy represents `deploy` method of Management native contract. func Deploy(script, manifest []byte) *Contract { - return contract.Call(interop.Hash160(Hash), "deploy", - contract.States|contract.AllowNotify, script, manifest).(*Contract) + return neogointernal.CallWithToken(Hash, "deploy", + int(contract.States|contract.AllowNotify), script, manifest).(*Contract) } // DeployWithData represents `deploy` method of Management native contract. func DeployWithData(script, manifest []byte, data interface{}) *Contract { - return contract.Call(interop.Hash160(Hash), "deploy", - contract.States|contract.AllowNotify, script, manifest, data).(*Contract) + return neogointernal.CallWithToken(Hash, "deploy", + int(contract.States|contract.AllowNotify), script, manifest, data).(*Contract) } // Destroy represents `destroy` method of Management native contract. func Destroy() { - contract.Call(interop.Hash160(Hash), "destroy", contract.States|contract.AllowNotify) + neogointernal.CallWithTokenNoRet(Hash, "destroy", int(contract.States|contract.AllowNotify)) } // GetContract represents `getContract` method of Management native contract. func GetContract(addr interop.Hash160) *Contract { - return contract.Call(interop.Hash160(Hash), "getContract", contract.ReadStates, addr).(*Contract) + return neogointernal.CallWithToken(Hash, "getContract", int(contract.ReadStates), addr).(*Contract) } // GetMinimumDeploymentFee represents `getMinimumDeploymentFee` method of Management native contract. func GetMinimumDeploymentFee() int { - return contract.Call(interop.Hash160(Hash), "getMinimumDeploymentFee", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "getMinimumDeploymentFee", int(contract.ReadStates)).(int) } // SetMinimumDeploymentFee represents `setMinimumDeploymentFee` method of Management native contract. func SetMinimumDeploymentFee(value int) { - contract.Call(interop.Hash160(Hash), "setMinimumDeploymentFee", contract.States, value) + neogointernal.CallWithTokenNoRet(Hash, "setMinimumDeploymentFee", int(contract.States), value) } // Update represents `update` method of Management native contract. func Update(script, manifest []byte) { - contract.Call(interop.Hash160(Hash), "update", - contract.States|contract.AllowNotify, script, manifest) + neogointernal.CallWithTokenNoRet(Hash, "update", + int(contract.States|contract.AllowNotify), script, manifest) } // UpdateWithData represents `update` method of Management native contract. func UpdateWithData(script, manifest []byte, data interface{}) { - contract.Call(interop.Hash160(Hash), "update", - contract.States|contract.AllowNotify, script, manifest, data) + neogointernal.CallWithTokenNoRet(Hash, "update", + int(contract.States|contract.AllowNotify), script, manifest, data) } diff --git a/pkg/interop/native/neo/neo.go b/pkg/interop/native/neo/neo.go index 47ff4b30f..4295c9a8b 100644 --- a/pkg/interop/native/neo/neo.go +++ b/pkg/interop/native/neo/neo.go @@ -9,6 +9,7 @@ package neo import ( "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) // AccountState contains info about NEO holder. @@ -23,86 +24,86 @@ const Hash = "\xf5\x63\xea\x40\xbc\x28\x3d\x4d\x0e\x05\xc4\x8e\xa3\x05\xb3\xf2\x // Symbol represents `symbol` method of NEO native contract. func Symbol() string { - return contract.Call(interop.Hash160(Hash), "symbol", contract.NoneFlag).(string) + return neogointernal.CallWithToken(Hash, "symbol", int(contract.NoneFlag)).(string) } // Decimals represents `decimals` method of NEO native contract. func Decimals() int { - return contract.Call(interop.Hash160(Hash), "decimals", contract.NoneFlag).(int) + return neogointernal.CallWithToken(Hash, "decimals", int(contract.NoneFlag)).(int) } // TotalSupply represents `totalSupply` method of NEO native contract. func TotalSupply() int { - return contract.Call(interop.Hash160(Hash), "totalSupply", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "totalSupply", int(contract.ReadStates)).(int) } // BalanceOf represents `balanceOf` method of NEO native contract. func BalanceOf(addr interop.Hash160) int { - return contract.Call(interop.Hash160(Hash), "balanceOf", contract.ReadStates, addr).(int) + return neogointernal.CallWithToken(Hash, "balanceOf", int(contract.ReadStates), addr).(int) } // Transfer represents `transfer` method of NEO native contract. func Transfer(from, to interop.Hash160, amount int, data interface{}) bool { - return contract.Call(interop.Hash160(Hash), "transfer", - contract.All, from, to, amount, data).(bool) + return neogointernal.CallWithToken(Hash, "transfer", + int(contract.All), from, to, amount, data).(bool) } // GetCommittee represents `getCommittee` method of NEO native contract. func GetCommittee() []interop.PublicKey { - return contract.Call(interop.Hash160(Hash), "getCommittee", contract.ReadStates).([]interop.PublicKey) + return neogointernal.CallWithToken(Hash, "getCommittee", int(contract.ReadStates)).([]interop.PublicKey) } // GetCandidates represents `getCandidates` method of NEO native contract. func GetCandidates() []interop.PublicKey { - return contract.Call(interop.Hash160(Hash), "getCandidates", contract.ReadStates).([]interop.PublicKey) + return neogointernal.CallWithToken(Hash, "getCandidates", int(contract.ReadStates)).([]interop.PublicKey) } // GetNextBlockValidators represents `getNextBlockValidators` method of NEO native contract. func GetNextBlockValidators() []interop.PublicKey { - return contract.Call(interop.Hash160(Hash), "getNextBlockValidators", contract.ReadStates).([]interop.PublicKey) + return neogointernal.CallWithToken(Hash, "getNextBlockValidators", int(contract.ReadStates)).([]interop.PublicKey) } // GetGASPerBlock represents `getGasPerBlock` method of NEO native contract. func GetGASPerBlock() int { - return contract.Call(interop.Hash160(Hash), "getGasPerBlock", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "getGasPerBlock", int(contract.ReadStates)).(int) } // SetGASPerBlock represents `setGasPerBlock` method of NEO native contract. func SetGASPerBlock(amount int) { - contract.Call(interop.Hash160(Hash), "setGasPerBlock", contract.States, amount) + neogointernal.CallWithTokenNoRet(Hash, "setGasPerBlock", int(contract.States), amount) } // GetRegisterPrice represents `getRegisterPrice` method of NEO native contract. func GetRegisterPrice() int { - return contract.Call(interop.Hash160(Hash), "getRegisterPrice", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "getRegisterPrice", int(contract.ReadStates)).(int) } // SetRegisterPrice represents `setRegisterPrice` method of NEO native contract. func SetRegisterPrice(amount int) { - contract.Call(interop.Hash160(Hash), "setRegisterPrice", contract.States, amount) + neogointernal.CallWithTokenNoRet(Hash, "setRegisterPrice", int(contract.States), amount) } // RegisterCandidate represents `registerCandidate` method of NEO native contract. func RegisterCandidate(pub interop.PublicKey) bool { - return contract.Call(interop.Hash160(Hash), "registerCandidate", contract.States, pub).(bool) + return neogointernal.CallWithToken(Hash, "registerCandidate", int(contract.States), pub).(bool) } // UnregisterCandidate represents `unregisterCandidate` method of NEO native contract. func UnregisterCandidate(pub interop.PublicKey) bool { - return contract.Call(interop.Hash160(Hash), "unregisterCandidate", contract.States, pub).(bool) + return neogointernal.CallWithToken(Hash, "unregisterCandidate", int(contract.States), pub).(bool) } // Vote represents `vote` method of NEO native contract. func Vote(addr interop.Hash160, pub interop.PublicKey) bool { - return contract.Call(interop.Hash160(Hash), "vote", contract.States, addr, pub).(bool) + return neogointernal.CallWithToken(Hash, "vote", int(contract.States), addr, pub).(bool) } // UnclaimedGAS represents `unclaimedGas` method of NEO native contract. func UnclaimedGAS(addr interop.Hash160, end int) int { - return contract.Call(interop.Hash160(Hash), "unclaimedGas", contract.ReadStates, addr, end).(int) + return neogointernal.CallWithToken(Hash, "unclaimedGas", int(contract.ReadStates), addr, end).(int) } // GetAccountState represents `getAccountState` method of NEO native contract. func GetAccountState(addr interop.Hash160) *AccountState { - return contract.Call(interop.Hash160(Hash), "getAccountState", contract.ReadStates, addr).(*AccountState) + return neogointernal.CallWithToken(Hash, "getAccountState", int(contract.ReadStates), addr).(*AccountState) } diff --git a/pkg/interop/native/notary/notary.go b/pkg/interop/native/notary/notary.go index 67c2861d7..d34320583 100644 --- a/pkg/interop/native/notary/notary.go +++ b/pkg/interop/native/notary/notary.go @@ -8,6 +8,7 @@ package notary import ( "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) // Hash represents Notary contract hash. @@ -15,32 +16,32 @@ const Hash = "\x3b\xec\x35\x31\x11\x9b\xba\xd7\x6d\xd0\x44\x92\x0b\x0d\xe6\xc3\x // LockDepositUntil represents `lockDepositUntil` method of Notary native contract. func LockDepositUntil(addr interop.Hash160, till int) bool { - return contract.Call(interop.Hash160(Hash), "lockDepositUntil", contract.States, + return neogointernal.CallWithToken(Hash, "lockDepositUntil", int(contract.States), addr, till).(bool) } // Withdraw represents `withdraw` method of Notary native contract. func Withdraw(from, to interop.Hash160) bool { - return contract.Call(interop.Hash160(Hash), "withdraw", contract.States, + return neogointernal.CallWithToken(Hash, "withdraw", int(contract.States), from, to).(bool) } // BalanceOf represents `balanceOf` method of Notary native contract. func BalanceOf(addr interop.Hash160) int { - return contract.Call(interop.Hash160(Hash), "balanceOf", contract.ReadStates, addr).(int) + return neogointernal.CallWithToken(Hash, "balanceOf", int(contract.ReadStates), addr).(int) } // ExpirationOf represents `expirationOf` method of Notary native contract. func ExpirationOf(addr interop.Hash160) int { - return contract.Call(interop.Hash160(Hash), "expirationOf", contract.ReadStates, addr).(int) + return neogointernal.CallWithToken(Hash, "expirationOf", int(contract.ReadStates), addr).(int) } // GetMaxNotValidBeforeDelta represents `getMaxNotValidBeforeDelta` method of Notary native contract. func GetMaxNotValidBeforeDelta() int { - return contract.Call(interop.Hash160(Hash), "getMaxNotValidBeforeDelta", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "getMaxNotValidBeforeDelta", int(contract.ReadStates)).(int) } // SetMaxNotValidBeforeDelta represents `setMaxNotValidBeforeDelta` method of Notary native contract. func SetMaxNotValidBeforeDelta(value int) { - contract.Call(interop.Hash160(Hash), "setMaxNotValidBeforeDelta", contract.States, value) + neogointernal.CallWithTokenNoRet(Hash, "setMaxNotValidBeforeDelta", int(contract.States), value) } diff --git a/pkg/interop/native/oracle/oracle.go b/pkg/interop/native/oracle/oracle.go index d054539c3..036a4a7e9 100644 --- a/pkg/interop/native/oracle/oracle.go +++ b/pkg/interop/native/oracle/oracle.go @@ -6,8 +6,8 @@ protocols. package oracle import ( - "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) // These are potential response codes you get in your callback completing @@ -66,18 +66,18 @@ const MinimumResponseGas = 10_000_000 // so it should be enough to pay for reply data as well as // its processing. func Request(url string, filter []byte, cb string, userData interface{}, gasForResponse int) { - contract.Call(interop.Hash160(Hash), "request", - contract.States|contract.AllowNotify, + neogointernal.CallWithTokenNoRet(Hash, "request", + int(contract.States|contract.AllowNotify), url, filter, cb, userData, gasForResponse) } // GetPrice returns current oracle request price. func GetPrice() int { - return contract.Call(interop.Hash160(Hash), "getPrice", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "getPrice", int(contract.ReadStates)).(int) } // SetPrice allows to set oracle request price. This method can only be // successfully invoked by the committee. func SetPrice(amount int) { - contract.Call(interop.Hash160(Hash), "setPrice", contract.States, amount) + neogointernal.CallWithTokenNoRet(Hash, "setPrice", int(contract.States), amount) } diff --git a/pkg/interop/native/policy/policy.go b/pkg/interop/native/policy/policy.go index 37272f81d..58eb45f72 100644 --- a/pkg/interop/native/policy/policy.go +++ b/pkg/interop/native/policy/policy.go @@ -7,6 +7,7 @@ package policy import ( "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) // Hash represents Policy contract hash. @@ -14,45 +15,45 @@ const Hash = "\x7b\xc6\x81\xc0\xa1\xf7\x1d\x54\x34\x57\xb6\x8b\xba\x8d\x5f\x9f\x // GetFeePerByte represents `getFeePerByte` method of Policy native contract. func GetFeePerByte() int { - return contract.Call(interop.Hash160(Hash), "getFeePerByte", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "getFeePerByte", int(contract.ReadStates)).(int) } // SetFeePerByte represents `setFeePerByte` method of Policy native contract. func SetFeePerByte(value int) { - contract.Call(interop.Hash160(Hash), "setFeePerByte", contract.States, value) + neogointernal.CallWithTokenNoRet(Hash, "setFeePerByte", int(contract.States), value) } // GetExecFeeFactor represents `getExecFeeFactor` method of Policy native contract. func GetExecFeeFactor() int { - return contract.Call(interop.Hash160(Hash), "getExecFeeFactor", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "getExecFeeFactor", int(contract.ReadStates)).(int) } // SetExecFeeFactor represents `setExecFeeFactor` method of Policy native contract. func SetExecFeeFactor(value int) { - contract.Call(interop.Hash160(Hash), "setExecFeeFactor", contract.States, value) + neogointernal.CallWithTokenNoRet(Hash, "setExecFeeFactor", int(contract.States), value) } // GetStoragePrice represents `getStoragePrice` method of Policy native contract. func GetStoragePrice() int { - return contract.Call(interop.Hash160(Hash), "getStoragePrice", contract.ReadStates).(int) + return neogointernal.CallWithToken(Hash, "getStoragePrice", int(contract.ReadStates)).(int) } // SetStoragePrice represents `setStoragePrice` method of Policy native contract. func SetStoragePrice(value int) { - contract.Call(interop.Hash160(Hash), "setStoragePrice", contract.States, value) + neogointernal.CallWithTokenNoRet(Hash, "setStoragePrice", int(contract.States), value) } // IsBlocked represents `isBlocked` method of Policy native contract. func IsBlocked(addr interop.Hash160) bool { - return contract.Call(interop.Hash160(Hash), "isBlocked", contract.ReadStates, addr).(bool) + return neogointernal.CallWithToken(Hash, "isBlocked", int(contract.ReadStates), addr).(bool) } // BlockAccount represents `blockAccount` method of Policy native contract. func BlockAccount(addr interop.Hash160) bool { - return contract.Call(interop.Hash160(Hash), "blockAccount", contract.States, addr).(bool) + return neogointernal.CallWithToken(Hash, "blockAccount", int(contract.States), addr).(bool) } // UnblockAccount represents `unblockAccount` method of Policy native contract. func UnblockAccount(addr interop.Hash160) bool { - return contract.Call(interop.Hash160(Hash), "unblockAccount", contract.States, addr).(bool) + return neogointernal.CallWithToken(Hash, "unblockAccount", int(contract.States), addr).(bool) } diff --git a/pkg/interop/native/roles/roles.go b/pkg/interop/native/roles/roles.go index 438150768..da4134f3b 100644 --- a/pkg/interop/native/roles/roles.go +++ b/pkg/interop/native/roles/roles.go @@ -8,6 +8,7 @@ package roles import ( "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) // Hash represents RoleManagement contract hash. @@ -27,12 +28,12 @@ const ( // GetDesignatedByRole represents `getDesignatedByRole` method of RoleManagement native contract. func GetDesignatedByRole(r Role, height uint32) []interop.PublicKey { - return contract.Call(interop.Hash160(Hash), "getDesignatedByRole", - contract.ReadStates, r, height).([]interop.PublicKey) + return neogointernal.CallWithToken(Hash, "getDesignatedByRole", + int(contract.ReadStates), r, height).([]interop.PublicKey) } // DesignateAsRole represents `designateAsRole` method of RoleManagement native contract. func DesignateAsRole(r Role, pubs []interop.PublicKey) { - contract.Call(interop.Hash160(Hash), "designateAsRole", - contract.States|contract.AllowNotify, r, pubs) + neogointernal.CallWithTokenNoRet(Hash, "designateAsRole", + int(contract.States|contract.AllowNotify), r, pubs) } diff --git a/pkg/interop/native/std/std.go b/pkg/interop/native/std/std.go index dc09d1183..274f34d9f 100644 --- a/pkg/interop/native/std/std.go +++ b/pkg/interop/native/std/std.go @@ -5,8 +5,8 @@ It implements various useful conversion functions. package std import ( - "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) // Hash represents StdLib contract hash. @@ -17,14 +17,14 @@ const Hash = "\xc0\xef\x39\xce\xe0\xe4\xe9\x25\xc6\xc2\xa0\x6a\x79\xe1\x44\x0d\x // from interop package) and allows to save them in storage or pass into Notify // and then Deserialize them on the next run or in the external event receiver. func Serialize(item interface{}) []byte { - return contract.Call(interop.Hash160(Hash), "serialize", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "serialize", int(contract.NoneFlag), item).([]byte) } // Deserialize calls `deserialize` method of StdLib native contract and unpacks // previously serialized value from a byte slice, it's the opposite of Serialize. func Deserialize(b []byte) interface{} { - return contract.Call(interop.Hash160(Hash), "deserialize", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "deserialize", int(contract.NoneFlag), b) } @@ -39,7 +39,7 @@ func Deserialize(b []byte) interface{} { // []interface{} -> json array // map[type1]type2 -> json object with string keys marshaled as strings (not base64). func JSONSerialize(item interface{}) []byte { - return contract.Call(interop.Hash160(Hash), "jsonSerialize", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "jsonSerialize", int(contract.NoneFlag), item).([]byte) } @@ -52,7 +52,7 @@ func JSONSerialize(item interface{}) []byte { // arrays -> []interface{} // maps -> map[string]interface{} func JSONDeserialize(data []byte) interface{} { - return contract.Call(interop.Hash160(Hash), "jsonDeserialize", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "jsonDeserialize", int(contract.NoneFlag), data) } @@ -60,14 +60,14 @@ func JSONDeserialize(data []byte) interface{} { // given byte slice into a base64 string and returns byte representation of this // string. func Base64Encode(b []byte) string { - return contract.Call(interop.Hash160(Hash), "base64Encode", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "base64Encode", int(contract.NoneFlag), b).(string) } // Base64Decode calls `base64Decode` method of StdLib native contract and decodes // given base64 string represented as a byte slice into byte slice. func Base64Decode(b []byte) []byte { - return contract.Call(interop.Hash160(Hash), "base64Decode", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "base64Decode", int(contract.NoneFlag), b).([]byte) } @@ -75,14 +75,14 @@ func Base64Decode(b []byte) []byte { // given byte slice into a base58 string and returns byte representation of this // string. func Base58Encode(b []byte) string { - return contract.Call(interop.Hash160(Hash), "base58Encode", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "base58Encode", int(contract.NoneFlag), b).(string) } // Base58Decode calls `base58Decode` method of StdLib native contract and decodes // given base58 string represented as a byte slice into a new byte slice. func Base58Decode(b []byte) []byte { - return contract.Call(interop.Hash160(Hash), "base58Decode", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "base58Decode", int(contract.NoneFlag), b).([]byte) } @@ -90,42 +90,42 @@ func Base58Decode(b []byte) []byte { // given byte slice into a base58 string with checksum and returns byte representation of this // string. func Base58CheckEncode(b []byte) string { - return contract.Call(interop.Hash160(Hash), "base58CheckEncode", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "base58CheckEncode", int(contract.NoneFlag), b).(string) } // Base58CheckDecode calls `base58CheckDecode` method of StdLib native contract and decodes // given base58 string with a checksum represented as a byte slice into a new byte slice. func Base58CheckDecode(b []byte) []byte { - return contract.Call(interop.Hash160(Hash), "base58CheckDecode", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "base58CheckDecode", int(contract.NoneFlag), b).([]byte) } // Itoa converts num in a given base to string. Base should be either 10 or 16. // It uses `itoa` method of StdLib native contract. func Itoa(num int, base int) string { - return contract.Call(interop.Hash160(Hash), "itoa", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "itoa", int(contract.NoneFlag), num, base).(string) } // Itoa10 converts num in a base 10 to string. // It uses `itoa` method of StdLib native contract. func Itoa10(num int) string { - return contract.Call(interop.Hash160(Hash), "itoa", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "itoa", int(contract.NoneFlag), num).(string) } // Atoi converts string to a number in a given base. Base should be either 10 or 16. // It uses `atoi` method of StdLib native contract. func Atoi(s string, base int) int { - return contract.Call(interop.Hash160(Hash), "atoi", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "atoi", int(contract.NoneFlag), s, base).(int) } // Atoi10 converts string to a number in a base 10. // It uses `atoi` method of StdLib native contract. func Atoi10(s string) int { - return contract.Call(interop.Hash160(Hash), "atoi", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "atoi", int(contract.NoneFlag), s).(int) } @@ -133,41 +133,41 @@ func Atoi10(s string) int { // The result will be 0 if a==b, -1 if a < b, and +1 if a > b. // It uses `memoryCompare` method of StdLib native contract. func MemoryCompare(s1, s2 []byte) int { - return contract.Call(interop.Hash160(Hash), "memoryCompare", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "memoryCompare", int(contract.NoneFlag), s1, s2).(int) } // MemorySearch returns index of the first occurrence of val in mem. // If not found, -1 is returned. It uses `memorySearch` method of StdLib native contract. func MemorySearch(mem, pattern []byte) int { - return contract.Call(interop.Hash160(Hash), "memorySearch", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "memorySearch", int(contract.NoneFlag), mem, pattern).(int) } // MemorySearchIndex returns index of the first occurrence of val in mem starting from start. // If not found, -1 is returned. It uses `memorySearch` method of StdLib native contract. func MemorySearchIndex(mem, pattern []byte, start int) int { - return contract.Call(interop.Hash160(Hash), "memorySearch", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "memorySearch", int(contract.NoneFlag), mem, pattern, start).(int) } // MemorySearchLastIndex returns index of the last occurrence of val in mem ending before start. // If not found, -1 is returned. It uses `memorySearch` method of StdLib native contract. func MemorySearchLastIndex(mem, pattern []byte, start int) int { - return contract.Call(interop.Hash160(Hash), "memorySearch", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "memorySearch", int(contract.NoneFlag), mem, pattern, start, true).(int) } // StringSplit splits s by occurrences of sep. // It uses `stringSplit` method of StdLib native contract. func StringSplit(s, sep string) []string { - return contract.Call(interop.Hash160(Hash), "stringSplit", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "stringSplit", int(contract.NoneFlag), s, sep).([]string) } // StringSplitNonEmpty splits s by occurrences of sep and returns a list of non-empty items. // It uses `stringSplit` method of StdLib native contract. func StringSplitNonEmpty(s, sep string) []string { - return contract.Call(interop.Hash160(Hash), "stringSplit", contract.NoneFlag, + return neogointernal.CallWithToken(Hash, "stringSplit", int(contract.NoneFlag), s, sep, true).([]string) } diff --git a/pkg/interop/neogointernal/call.go b/pkg/interop/neogointernal/call.go new file mode 100644 index 000000000..0309bfe94 --- /dev/null +++ b/pkg/interop/neogointernal/call.go @@ -0,0 +1,12 @@ +package neogointernal + +// CallWithToken performs contract call using CALLT instruction. It only works +// for static script hashes and methods, requiring additional metadata compared to +// ordinary contract.Call. It's more efficient though. +func CallWithToken(scriptHash string, method string, flags int, args ...interface{}) interface{} { + return nil +} + +// CallWithTokenNoRet is a version of CallWithToken that does not return anything. +func CallWithTokenNoRet(scriptHash string, method string, flags int, args ...interface{}) { +} diff --git a/pkg/neotest/compile.go b/pkg/neotest/compile.go index cd9831c8d..cca0c8c41 100644 --- a/pkg/neotest/compile.go +++ b/pkg/neotest/compile.go @@ -29,10 +29,7 @@ func CompileSource(t *testing.T, sender util.Uint160, src io.Reader, opts *compi // nef.NewFile() cares about version a lot. config.Version = "neotest" - avm, di, err := compiler.CompileWithDebugInfo(opts.Name, src) - require.NoError(t, err) - - ne, err := nef.NewFile(avm) + ne, di, err := compiler.CompileWithDebugInfo(opts.Name, src) require.NoError(t, err) m, err := compiler.CreateManifest(di, opts) @@ -54,10 +51,7 @@ func CompileFile(t *testing.T, sender util.Uint160, srcPath string, configPath s // nef.NewFile() cares about version a lot. config.Version = "neotest" - avm, di, err := compiler.CompileWithDebugInfo(srcPath, nil) - require.NoError(t, err) - - ne, err := nef.NewFile(avm) + ne, di, err := compiler.CompileWithDebugInfo(srcPath, nil) require.NoError(t, err) conf, err := smartcontract.ParseContractConfig(configPath) diff --git a/pkg/vm/cli/cli.go b/pkg/vm/cli/cli.go index c3b39e02a..fb4450090 100644 --- a/pkg/vm/cli/cli.go +++ b/pkg/vm/cli/cli.go @@ -408,7 +408,7 @@ func handleLoadGo(c *ishell.Context) { } setManifestInContext(c, m) - v.LoadWithFlags(b, callflag.All) + v.LoadWithFlags(b.Script, callflag.All) c.Printf("READY: loaded %d instructions\n", v.Context().LenInstr()) changePrompt(c, v) } diff --git a/pkg/vm/cli/cli_test.go b/pkg/vm/cli/cli_test.go index b6d266cef..435d647bb 100644 --- a/pkg/vm/cli/cli_test.go +++ b/pkg/vm/cli/cli_test.go @@ -22,7 +22,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" @@ -244,9 +243,7 @@ func TestLoad(t *testing.T) { t.Run("loadnef", func(t *testing.T) { config.Version = "0.92.0-test" - script, di, err := compiler.CompileWithDebugInfo("test", strings.NewReader(src)) - require.NoError(t, err) - nefFile, err := nef.NewFile(script) + nefFile, di, err := compiler.CompileWithDebugInfo("test", strings.NewReader(src)) require.NoError(t, err) filename := filepath.Join(tmpDir, "vmtestcontract.nef") rawNef, err := nefFile.Bytes()