From d2ddf7b7cb031daa502f8a830f9e5b5be70764fc Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 23 Jul 2020 18:13:02 +0300 Subject: [PATCH] *: support invoking methods by offset Allow to invoke methods by offset: 1. Every invoked contract must have manifest. 2. Check arguments count on invocation. 3. Change AppCall to a regular syscall. 4. Add test suite for `System.Contract.Call`. --- pkg/compiler/analysis.go | 1 - pkg/compiler/codegen.go | 7 - pkg/compiler/interop_test.go | 29 ++-- pkg/compiler/syscall.go | 3 + pkg/core/helper_test.go | 2 +- pkg/core/interop_system.go | 38 +++++- pkg/core/interop_system_test.go | 104 ++++++++++++++ pkg/core/native_contract_test.go | 5 +- pkg/interop/engine/engine.go | 2 +- pkg/rpc/server/server_test.go | 4 +- pkg/rpc/server/testdata/test_contract.go | 166 ++++++++++++----------- pkg/rpc/server/testdata/testblocks.acc | Bin 6846 -> 7315 bytes pkg/smartcontract/manifest/manifest.go | 10 ++ pkg/vm/vm.go | 18 +-- 14 files changed, 272 insertions(+), 117 deletions(-) diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index 34b648656..621825a61 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -16,7 +16,6 @@ var ( goBuiltins = []string{"len", "append", "panic"} // Custom builtin utility functions. customBuiltins = []string{ - "AppCall", "FromAddress", "Equals", "ToBool", "ToByteArray", "ToInteger", } diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 25d9a6e62..2dbcbc900 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1220,13 +1220,6 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { typ = stackitem.BooleanT } c.emitConvert(typ) - case "AppCall": - c.emitReverse(len(expr.Args)) - buf := c.getByteArray(expr.Args[0]) - if buf != nil && len(buf) != 20 { - c.prog.Err = errors.New("invalid script hash") - } - emit.Syscall(c.prog.BinWriter, "System.Contract.Call") case "Equals": emit.Opcode(c.prog.BinWriter, opcode.EQUAL) case "FromAddress": diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 6a8308924..8f7326637 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -74,17 +74,25 @@ func TestAppCall(t *testing.T) { srcInner := ` package foo func Main(a []byte, b []byte) []byte { + panic("Main was called") + } + func Append(a []byte, b []byte) []byte { return append(a, b...) } ` - inner, err := compiler.Compile(strings.NewReader(srcInner)) + inner, di, err := compiler.CompileWithDebugInfo(strings.NewReader(srcInner)) + require.NoError(t, err) + m, err := di.ConvertToManifest(smartcontract.NoProperties) require.NoError(t, err) - ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil, nil, zaptest.NewLogger(t)) - require.NoError(t, ic.DAO.PutContractState(&state.Contract{Script: inner})) - ih := hash.Hash160(inner) + ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet), nil, nil, nil, zaptest.NewLogger(t)) + require.NoError(t, ic.DAO.PutContractState(&state.Contract{ + Script: inner, + Manifest: *m, + })) + t.Run("valid script", func(t *testing.T) { src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE())) v := spawnVM(t, ic, src) @@ -102,13 +110,6 @@ func TestAppCall(t *testing.T) { require.Error(t, v.Run()) }) - t.Run("invalid script address", func(t *testing.T) { - src := getAppCallScript("[]byte{1, 2, 3}") - - _, err := compiler.Compile(strings.NewReader(src)) - require.Error(t, err) - }) - t.Run("convert from string constant", func(t *testing.T) { src := ` package foo @@ -117,7 +118,7 @@ func TestAppCall(t *testing.T) { func Main() []byte { x := []byte{1, 2} y := []byte{3, 4} - result := engine.AppCall([]byte(scriptHash), x, y) + result := engine.AppCall([]byte(scriptHash), "append", x, y) return result.([]byte) } ` @@ -136,7 +137,7 @@ func TestAppCall(t *testing.T) { x := []byte{1, 2} y := []byte{3, 4} var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `) - result := engine.AppCall(addr, x, y) + result := engine.AppCall(addr, "append", x, y) return result.([]byte) } ` @@ -155,7 +156,7 @@ func getAppCallScript(h string) string { func Main() []byte { x := []byte{1, 2} y := []byte{3, 4} - result := engine.AppCall(` + h + `, x, y) + result := engine.AppCall(` + h + `, "append", x, y) return result.([]byte) } ` diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index aa46aafe1..9f643bdd8 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -44,6 +44,9 @@ var syscalls = map[string]map[string]Syscall{ "Next": {"System.Enumerator.Next", false}, "Value": {"System.Enumerator.Value", false}, }, + "engine": { + "AppCall": {"System.Contract.Call", false}, + }, "iterator": { "Concat": {"System.Iterator.Concat", false}, "Create": {"System.Iterator.Create", false}, diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index a32ec6c8b..a58d76b1a 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -252,7 +252,7 @@ func TestCreateBasicChain(t *testing.T) { // Now invoke this contract. script = io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(script.BinWriter, hash.Hash160(avm), "Put", "testkey", "testvalue") + emit.AppCallWithOperationAndArgs(script.BinWriter, hash.Hash160(avm), "putValue", "testkey", "testvalue") txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor) txInv.Nonce = getNextNonce() diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 7ac8feb3f..698d391a0 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "math/big" + "strings" "unicode/utf8" "github.com/nspcc-dev/neo-go/pkg/core/block" @@ -492,16 +493,49 @@ func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, method stac if err != nil { return err } + name := string(bs) + if strings.HasPrefix(name, "_") { + return errors.New("invalid method name (starts with '_')") + } + md := cs.Manifest.ABI.GetMethod(name) + if md == nil { + return fmt.Errorf("method '%s' not found", name) + } curr, err := ic.DAO.GetContractState(v.GetCurrentScriptHash()) if err == nil { if !curr.Manifest.CanCall(&cs.Manifest, string(bs)) { return errors.New("disallowed method call") } } + + arr, ok := args.Value().([]stackitem.Item) + if !ok { + return errors.New("second argument must be an array") + } + if len(arr) != len(md.Parameters) { + return fmt.Errorf("invalid argument count: %d (expected %d)", len(arr), len(md.Parameters)) + } + ic.Invocations[u]++ v.LoadScriptWithHash(cs.Script, u, v.Context().GetCallFlags()&f) - v.Estack().PushVal(args) - v.Estack().PushVal(method) + var isNative bool + for i := range ic.Natives { + if ic.Natives[i].Metadata().Hash.Equals(u) { + isNative = true + break + } + } + if isNative { + v.Estack().PushVal(args) + v.Estack().PushVal(method) + } else { + for i := len(arr) - 1; i >= 0; i-- { + v.Estack().PushVal(arr[i]) + } + // use Jump not Call here because context was loaded in LoadScript above. + v.Jump(v.Context(), md.Offset) + } + return nil } diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index 1fb13b603..941e51529 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/require" @@ -323,6 +324,109 @@ func TestBlockchainGetContractState(t *testing.T) { }) } +func getTestContractState() *state.Contract { + script := []byte{ + byte(opcode.ABORT), // abort if no offset was provided + byte(opcode.ADD), byte(opcode.RET), + byte(opcode.PUSH7), byte(opcode.RET), + } + h := hash.Hash160(script) + m := manifest.NewManifest(h) + m.ABI.Methods = []manifest.Method{ + { + Name: "add", + Offset: 1, + Parameters: []manifest.Parameter{ + manifest.NewParameter("addend1", smartcontract.IntegerType), + manifest.NewParameter("addend2", smartcontract.IntegerType), + }, + ReturnType: smartcontract.IntegerType, + }, + { + Name: "ret7", + Offset: 3, + Parameters: []manifest.Parameter{}, + ReturnType: smartcontract.IntegerType, + }, + } + return &state.Contract{ + Script: script, + Manifest: *m, + ID: 42, + } +} + +func TestContractCall(t *testing.T) { + v, ic, bc := createVM(t) + defer bc.Close() + + cs := getTestContractState() + require.NoError(t, ic.DAO.PutContractState(cs)) + + currScript := []byte{byte(opcode.NOP)} + initVM := func(v *vm.VM) { + v.Istack().Clear() + v.Estack().Clear() + v.Load(currScript) + v.Estack().PushVal(42) // canary + } + + h := cs.Manifest.ABI.Hash + m := manifest.NewManifest(hash.Hash160(currScript)) + perm := manifest.NewPermission(manifest.PermissionHash, h) + perm.Methods.Add("add") + m.Permissions = append(m.Permissions, *perm) + + require.NoError(t, ic.DAO.PutContractState(&state.Contract{ + Script: currScript, + Manifest: *m, + ID: 123, + })) + + addArgs := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(2)}) + t.Run("Good", func(t *testing.T) { + initVM(v) + v.Estack().PushVal(addArgs) + v.Estack().PushVal("add") + v.Estack().PushVal(h.BytesBE()) + require.NoError(t, contractCall(ic, v)) + require.NoError(t, v.Run()) + require.Equal(t, 2, v.Estack().Len()) + require.Equal(t, big.NewInt(3), v.Estack().Pop().Value()) + require.Equal(t, big.NewInt(42), v.Estack().Pop().Value()) + }) + + t.Run("CallExInvalidFlag", func(t *testing.T) { + initVM(v) + v.Estack().PushVal(byte(0xFF)) + v.Estack().PushVal(addArgs) + v.Estack().PushVal("add") + v.Estack().PushVal(h.BytesBE()) + require.Error(t, contractCallEx(ic, v)) + }) + + runInvalid := func(args ...interface{}) func(t *testing.T) { + return func(t *testing.T) { + initVM(v) + for i := range args { + v.Estack().PushVal(args[i]) + } + require.Error(t, contractCall(ic, v)) + } + } + + t.Run("Invalid", func(t *testing.T) { + t.Run("Hash", runInvalid(addArgs, "add", h.BytesBE()[1:])) + t.Run("MissingHash", runInvalid(addArgs, "add", util.Uint160{}.BytesBE())) + t.Run("Method", runInvalid(addArgs, stackitem.NewInterop("add"), h.BytesBE())) + t.Run("MissingMethod", runInvalid(addArgs, "sub", h.BytesBE())) + t.Run("DisallowedMethod", runInvalid(stackitem.NewArray(nil), "ret7", h.BytesBE())) + t.Run("Arguments", runInvalid(1, "add", h.BytesBE())) + t.Run("NotEnoughArguments", runInvalid( + stackitem.NewArray([]stackitem.Item{stackitem.Make(1)}), "add", h.BytesBE())) + }) +} + func TestContractCreate(t *testing.T) { v, cs, ic, bc := createVMAndContractState(t) v.GasLimit = -1 diff --git a/pkg/core/native_contract_test.go b/pkg/core/native_contract_test.go index 929413a1f..c155abab6 100644 --- a/pkg/core/native_contract_test.go +++ b/pkg/core/native_contract_test.go @@ -95,7 +95,10 @@ func TestNativeContract_Invoke(t *testing.T) { tn := newTestNative() chain.registerNative(tn) - err := chain.dao.PutContractState(&state.Contract{Script: tn.meta.Script}) + err := chain.dao.PutContractState(&state.Contract{ + Script: tn.meta.Script, + Manifest: tn.meta.Manifest, + }) require.NoError(t, err) w := io.NewBufBinWriter() diff --git a/pkg/interop/engine/engine.go b/pkg/interop/engine/engine.go index d126a5b99..480e644d6 100644 --- a/pkg/interop/engine/engine.go +++ b/pkg/interop/engine/engine.go @@ -13,6 +13,6 @@ package engine // dynamic calls in Neo (contracts should have a special property declared // and paid for to be able to use dynamic calls). This function uses // `System.Contract.Call` syscall. -func AppCall(scriptHash []byte, args ...interface{}) interface{} { +func AppCall(scriptHash []byte, method string, args ...interface{}) interface{} { return nil } diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 6c1b2f846..03d5ee7ec 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -51,8 +51,8 @@ type rpcTestCase struct { check func(t *testing.T, e *executor, result interface{}) } -const testContractHash = "402da558b87b5e54b59dc242c788bb4dd4cd906c" -const deploymentTxHash = "2afd69cc80ebe900a060450e8628b57063f3ec93ca5fc7f94582be4a4f3a041f" +const testContractHash = "6e2d823c81589871590653a100c7e9bdf9c94344" +const deploymentTxHash = "3b434127495a6dd0e786a2e0f04696009cd6e6e5f9b930f0e79356638532096c" var rpcTestCases = map[string][]rpcTestCase{ "getapplicationlog": { diff --git a/pkg/rpc/server/testdata/test_contract.go b/pkg/rpc/server/testdata/test_contract.go index af08ea213..c7da44f4c 100644 --- a/pkg/rpc/server/testdata/test_contract.go +++ b/pkg/rpc/server/testdata/test_contract.go @@ -11,83 +11,91 @@ const ( ) func Main(operation string, args []interface{}) interface{} { - runtime.Notify("contract call", operation, args) - switch operation { - case "Put": - ctx := storage.GetContext() - storage.Put(ctx, args[0].([]byte), args[1].([]byte)) - return true - case "totalSupply": - return totalSupply - case "decimals": - return decimals - case "name": - return "Rubl" - case "symbol": - return "RUB" - case "balanceOf": - ctx := storage.GetContext() - addr := args[0].([]byte) - if len(addr) != 20 { - runtime.Log("invalid address") - return false - } - var amount int - val := storage.Get(ctx, addr) - if val != nil { - amount = val.(int) - } - runtime.Notify("balanceOf", addr, amount) - return amount - case "transfer": - ctx := storage.GetContext() - from := args[0].([]byte) - if len(from) != 20 { - runtime.Log("invalid 'from' address") - return false - } - to := args[1].([]byte) - if len(to) != 20 { - runtime.Log("invalid 'to' address") - return false - } - amount := args[2].(int) - if amount < 0 { - runtime.Log("invalid amount") - return false - } - - var fromBalance int - val := storage.Get(ctx, from) - if val != nil { - fromBalance = val.(int) - } - if fromBalance < amount { - runtime.Log("insufficient funds") - return false - } - fromBalance -= amount - storage.Put(ctx, from, fromBalance) - - var toBalance int - val = storage.Get(ctx, to) - if val != nil { - toBalance = val.(int) - } - toBalance += amount - storage.Put(ctx, to, toBalance) - - runtime.Notify("transfer", from, to, amount) - - return true - case "init": - ctx := storage.GetContext() - h := runtime.GetExecutingScriptHash() - amount := totalSupply - storage.Put(ctx, h, amount) - runtime.Notify("transfer", []byte{}, h, amount) - return true - default: - panic("invalid operation") - } + panic("invoking via Main is no longer supported") // catch possible bugs +} + +func Init() bool { + ctx := storage.GetContext() + h := runtime.GetExecutingScriptHash() + amount := totalSupply + storage.Put(ctx, h, amount) + runtime.Notify("transfer", []byte{}, h, amount) + return true +} + +func Transfer(from, to []byte, amount int) bool { + ctx := storage.GetContext() + if len(from) != 20 { + runtime.Log("invalid 'from' address") + return false + } + if len(to) != 20 { + runtime.Log("invalid 'to' address") + return false + } + if amount < 0 { + runtime.Log("invalid amount") + return false + } + + var fromBalance int + val := storage.Get(ctx, from) + if val != nil { + fromBalance = val.(int) + } + if fromBalance < amount { + runtime.Log("insufficient funds") + return false + } + fromBalance -= amount + storage.Put(ctx, from, fromBalance) + + var toBalance int + val = storage.Get(ctx, to) + if val != nil { + toBalance = val.(int) + } + toBalance += amount + storage.Put(ctx, to, toBalance) + + runtime.Notify("transfer", from, to, amount) + + return true +} + +func BalanceOf(addr []byte) int { + ctx := storage.GetContext() + if len(addr) != 20 { + runtime.Log("invalid address") + return 0 + } + var amount int + val := storage.Get(ctx, addr) + if val != nil { + amount = val.(int) + } + runtime.Notify("balanceOf", addr, amount) + return amount +} + +func Name() string { + return "Rubl" +} + +func Symbol() string { + return "RUB" +} + +func Decimals() int { + return decimals +} + +func TotalSupply() int { + return totalSupply +} + +func PutValue(key []byte, value []byte) bool { + ctx := storage.GetContext() + storage.Put(ctx, key, value) + return true } diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index f068d8f477da070189062f2b2b90188601b04ca9..e53450d08b61b176b5336b11d1ff9b754e092a79 100644 GIT binary patch delta 4071 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 delta 3553 zcmaJ@c|25m8@JDlIb+X~eaKJ%peTOfNWl z!J_d5-S6et+jBvw>UHhaKK3|&uk)2FvZr_7NR~V*wUEPffa34TC?}h`XC&SXE}v&!Uu^vjpj00ROFGd5QA=AO zmN$74DwWq-3;V?98+u1S4?FF1KJG~}8q=IuOHVubCN4j9_SjS#IvhZ5wa+%ZKFtlR z@gX<1>Uf?FDb;A07#n}hcK!{=i)NnAvV9Q@&UUzWXDPPau6~K;`_3;9cRZ4RT#yy%FGMjA{Ji0EZECF>-dbKlqbKl{3(#=*9MgA zkZ5tMdk&b&Xf{7^NY{1n+RJWJm;;axXTz>82c-u;X=;P1bgtZLkXbyDjL!q9$G*66 zm-W1)z2s$xS(lAv%DeOHmA>W$X#fGmvN;Jdqtyk$kO&tDfONciFrr~$GH~k6wM$ZA zIEl)#{vWR388@?>{oQKN(pi z;F)t!9m=^3)`_%G(-TsCRYLIiMwftLMC0LD90|T99v+URg~PYN%~+OZ76h!7l?~2j zGtP==Wr?%0CEzVAE%2LdY;ZVB0>Khbur;&B!;RrY3Oy#yi%O!<1qc*u6p@eMfhAGk zoKMoKwoycSIF-QHqsK)HBtG;Q5+w|tVEngkG&TksMf;_JiiyF-!4o(b$XY1;yEzTM zC5U4N?2@1`$Yf3#V9Zt(_Y?Ei7Xx5;Dt~`0p00z(lF6(%FNIbw)G1LaR+oha#Jw2w zT!aBXPSojzuwLEh@Hn~NA+tDn{>=yV#!fX!*GV3!*CQq(f(D>;DjiGqVMIrh<8l%5 zd?P0@q`j;NQtz@*4ki%sq$n(zmWzOiu=!F2q+YgAA|VJm7oi|90VKQ`I5JXizfdAY zi;Kch$v@x#aqnIBNWBoD2!g|su@pRUXJ{^h`L~C^3muG!Ky&payWkQ|aw1AM5W0LZ zAWNdeV#y?e4wgWOA+l*S)T43Nq7>!_;f-U6v98*5GWQ2kl0knk1WeEp;1)O|3HRP zp;Yi$p#~ND!{8S)6e@@c_EG#!rAfA-k1aBJ>r*J-Yya3 zAHPPAmgG(+1%3o2x1Z5gxg6oPi|qIQVa0=scItA9E&~M(jHKCO|M|-W=mfQ+S+o!U zDFw|y=|kGR38fbQ=fP z+(hSD?Fq+-FDsAR3{FB)NRG*$7sU(L!B!LE-ZXDUpW}gLfe^jWG|RHT2W+Xl4%>n) z7z83B-v_KU0HG6UbbjkHhyX~ij3mY>v(-j^9+3HsoSTr_*}Aez`74I_H1_Sm)gQ#GG1Q+{&%ZZpGKgGq0!W*HoQW`a~0d zT~rVmTX~67s-OdL|8aIo2+z)=?nt+B%^Im*qn4&Qn~Xoapk(4R6XkgQl?T-5~sm`=A#@5R&MQL3voPeT9L)9*WJzm{6E8j`Ewp6~jlw_sDPIqYrkX8=q zTj$4XOG?5%xsD&EzgRa!m$QGgV^+;q%2#7C>2k`{rd$?Hb)9iv4hf{Ze9Xif<=xPA z4?O=^gk1u~Z~+ZqJAbD3dWMp`sH=eu>hOn|udF?w9dLIIElyqWbU5&vTiH_n=mz{3W*%r; z?j65^mTv~9RJD2^T?E|%kcoasM$uxy+FeWSQB?!g_A|s`m&nui*Yw#g;yz z4KHGakuP-I=RM_tobOssqq~axGcHM{=vT2*G*toQKB}tKmAYnmUg01Y)Z;mfwZqhY z)^>z1Zm7Kq?bN|F+PVEv>U&9Mvh= zIh+WLf9${fF#&C z36TzSWWE-13=`x3pQzaG{l6{Y6Q&6t%1_*0v|}Ap^JDPBgPYTd{$J}D%1P^&D32_? z9*gg4|0~vF#wvx|rZ$>cem=w;Z>%Oy* zuV^6Fpt7?nikl6eRU;M&0%dH&CZ*FBp^G6^%~Zs1MUF#5DVPtk+tdgI-6A&Rb%&M2FvJ?bdaV z<@X)6bSQhI!OYqzmys&#z9(Wf;YGFh2#ICWHy+Yogn#zX7K2+CYZ`A@4b?l~cm$>U z$Tl;sd!5vv@2g2qHe}^CfIKt;x1fwf#*9U2wd_`6&R)pid zr>roQ4EXA*Z*GmTABJpCnpY|dUJW?2J$8f9GQjOlVAd~+ArGWVh diff --git a/pkg/smartcontract/manifest/manifest.go b/pkg/smartcontract/manifest/manifest.go index 307a6f2e6..d733c90c9 100644 --- a/pkg/smartcontract/manifest/manifest.go +++ b/pkg/smartcontract/manifest/manifest.go @@ -68,6 +68,16 @@ func DefaultManifest(h util.Uint160) *Manifest { return m } +// GetMethod returns methods with the specified name. +func (a *ABI) GetMethod(name string) *Method { + for i := range a.Methods { + if a.Methods[i].Name == name { + return &a.Methods[i] + } + } + return nil +} + // CanCall returns true is current contract is allowed to call // method of another contract. func (m *Manifest) CanCall(toCall *Manifest, method string) bool { diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index ea124a795..d5ef55f7f 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -1232,7 +1232,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro } if cond { - v.jump(ctx, offset) + v.Jump(ctx, offset) } case opcode.CALL, opcode.CALLL: @@ -1243,7 +1243,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro v.istack.PushVal(newCtx) offset := v.getJumpOffset(newCtx, parameter) - v.jump(newCtx, offset) + v.Jump(newCtx, offset) case opcode.CALLA: ptr := v.estack.Pop().Item().(*stackitem.Pointer) @@ -1255,7 +1255,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro newCtx.local = nil newCtx.arguments = nil v.istack.PushVal(newCtx) - v.jump(newCtx, ptr.Position()) + v.Jump(newCtx, ptr.Position()) case opcode.SYSCALL: interopID := GetInteropID(parameter) @@ -1404,7 +1404,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro } else { ctx.tryStack.Pop() } - v.jump(ctx, eOffset) + v.Jump(ctx, eOffset) case opcode.ENDFINALLY: if v.uncaughtException != nil { @@ -1412,7 +1412,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro return } eCtx := ctx.tryStack.Pop().Value().(*exceptionHandlingContext) - v.jump(ctx, eCtx.EndOffset) + v.Jump(ctx, eCtx.EndOffset) default: panic(fmt.Sprintf("unknown opcode %s", op.String())) @@ -1468,8 +1468,8 @@ func (v *VM) throw(item stackitem.Item) { v.handleException() } -// jump performs jump to the offset. -func (v *VM) jump(ctx *Context, offset int) { +// Jump performs jump to the offset. +func (v *VM) Jump(ctx *Context, offset int) { ctx.nextip = offset } @@ -1526,10 +1526,10 @@ func (v *VM) handleException() { ectx.State = eCatch v.estack.PushVal(v.uncaughtException) v.uncaughtException = nil - v.jump(ictx, ectx.CatchOffset) + v.Jump(ictx, ectx.CatchOffset) } else { ectx.State = eFinally - v.jump(ictx, ectx.FinallyOffset) + v.Jump(ictx, ectx.FinallyOffset) } return }