From ff1545b7eb3992bc653b4c7b2b51c81db7d7cd86 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 8 Jun 2022 16:52:30 +0300 Subject: [PATCH] core: move TestContractCall into the contract package It belongs there and now it can be moved. No functional changes. --- pkg/core/interop/contract/call_test.go | 157 +++++++++++++++++++++++++ pkg/core/interop_system_core_test.go | 140 ---------------------- 2 files changed, 157 insertions(+), 140 deletions(-) diff --git a/pkg/core/interop/contract/call_test.go b/pkg/core/interop/contract/call_test.go index 155a9cb90..474586b83 100644 --- a/pkg/core/interop/contract/call_test.go +++ b/pkg/core/interop/contract/call_test.go @@ -2,19 +2,27 @@ package contract_test import ( "math/big" + "path/filepath" "testing" + "github.com/nspcc-dev/neo-go/internal/contracts" + "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/neotest/chain" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "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/opcode" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) +var pathToInternalContracts = filepath.Join("..", "..", "..", "..", "internal", "contracts") + func TestGetCallFlags(t *testing.T) { bc, _ := chain.NewSingle(t) ic := bc.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{}) @@ -23,3 +31,152 @@ func TestGetCallFlags(t *testing.T) { require.NoError(t, contract.GetCallFlags(ic)) require.Equal(t, int64(callflag.All), ic.VM.Estack().Pop().Value().(*big.Int).Int64()) } + +func TestCall(t *testing.T) { + bc, _ := chain.NewSingle(t) + ic := bc.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{}) + + cs, currCs := contracts.GetTestContractState(t, pathToInternalContracts, 4, 5, random.Uint160()) // sender and IDs are not important for the test + require.NoError(t, native.PutContractState(ic.DAO, cs)) + require.NoError(t, native.PutContractState(ic.DAO, currCs)) + + currScript := currCs.NEF.Script + h := cs.Hash + + addArgs := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(2)}) + t.Run("Good", func(t *testing.T) { + t.Run("2 arguments", func(t *testing.T) { + loadScript(ic, currScript, 42) + ic.VM.Estack().PushVal(addArgs) + ic.VM.Estack().PushVal(callflag.All) + ic.VM.Estack().PushVal("add") + ic.VM.Estack().PushVal(h.BytesBE()) + require.NoError(t, contract.Call(ic)) + require.NoError(t, ic.VM.Run()) + require.Equal(t, 2, ic.VM.Estack().Len()) + require.Equal(t, big.NewInt(3), ic.VM.Estack().Pop().Value()) + require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value()) + }) + t.Run("3 arguments", func(t *testing.T) { + loadScript(ic, currScript, 42) + ic.VM.Estack().PushVal(stackitem.NewArray( + append(addArgs.Value().([]stackitem.Item), stackitem.Make(3)))) + ic.VM.Estack().PushVal(callflag.All) + ic.VM.Estack().PushVal("add") + ic.VM.Estack().PushVal(h.BytesBE()) + require.NoError(t, contract.Call(ic)) + require.NoError(t, ic.VM.Run()) + require.Equal(t, 2, ic.VM.Estack().Len()) + require.Equal(t, big.NewInt(6), ic.VM.Estack().Pop().Value()) + require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value()) + }) + }) + + t.Run("CallExInvalidFlag", func(t *testing.T) { + loadScript(ic, currScript, 42) + ic.VM.Estack().PushVal(addArgs) + ic.VM.Estack().PushVal(byte(0xFF)) + ic.VM.Estack().PushVal("add") + ic.VM.Estack().PushVal(h.BytesBE()) + require.Error(t, contract.Call(ic)) + }) + + runInvalid := func(args ...interface{}) func(t *testing.T) { + return func(t *testing.T) { + loadScriptWithHashAndFlags(ic, currScript, h, callflag.All, 42) + for i := range args { + ic.VM.Estack().PushVal(args[i]) + } + // interops can both return error and panic, + // we don't care which kind of error has occurred + require.Panics(t, func() { + err := contract.Call(ic) + if err != nil { + panic(err) + } + }) + } + } + + 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())) + t.Run("TooMuchArguments", runInvalid( + stackitem.NewArray([]stackitem.Item{ + stackitem.Make(1), stackitem.Make(2), stackitem.Make(3), stackitem.Make(4)}), + "add", h.BytesBE())) + }) + + t.Run("ReturnValues", func(t *testing.T) { + t.Run("Many", func(t *testing.T) { + loadScript(ic, currScript, 42) + ic.VM.Estack().PushVal(stackitem.NewArray(nil)) + ic.VM.Estack().PushVal(callflag.All) + ic.VM.Estack().PushVal("invalidReturn") + ic.VM.Estack().PushVal(h.BytesBE()) + require.NoError(t, contract.Call(ic)) + require.Error(t, ic.VM.Run()) + }) + t.Run("Void", func(t *testing.T) { + loadScript(ic, currScript, 42) + ic.VM.Estack().PushVal(stackitem.NewArray(nil)) + ic.VM.Estack().PushVal(callflag.All) + ic.VM.Estack().PushVal("justReturn") + ic.VM.Estack().PushVal(h.BytesBE()) + require.NoError(t, contract.Call(ic)) + require.NoError(t, ic.VM.Run()) + require.Equal(t, 2, ic.VM.Estack().Len()) + require.Equal(t, stackitem.Null{}, ic.VM.Estack().Pop().Item()) + require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value()) + }) + }) + + t.Run("IsolatedStack", func(t *testing.T) { + loadScript(ic, currScript, 42) + ic.VM.Estack().PushVal(stackitem.NewArray(nil)) + ic.VM.Estack().PushVal(callflag.All) + ic.VM.Estack().PushVal("drop") + ic.VM.Estack().PushVal(h.BytesBE()) + require.NoError(t, contract.Call(ic)) + require.Error(t, ic.VM.Run()) + }) + + t.Run("CallInitialize", func(t *testing.T) { + t.Run("Directly", runInvalid(stackitem.NewArray([]stackitem.Item{}), "_initialize", h.BytesBE())) + + loadScript(ic, currScript, 42) + ic.VM.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(5)})) + ic.VM.Estack().PushVal(callflag.All) + ic.VM.Estack().PushVal("add3") + ic.VM.Estack().PushVal(h.BytesBE()) + require.NoError(t, contract.Call(ic)) + require.NoError(t, ic.VM.Run()) + require.Equal(t, 2, ic.VM.Estack().Len()) + require.Equal(t, big.NewInt(8), ic.VM.Estack().Pop().Value()) + require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value()) + }) +} + +func loadScript(ic *interop.Context, script []byte, args ...interface{}) { + ic.SpawnVM() + ic.VM.LoadScriptWithFlags(script, callflag.AllowCall) + for i := range args { + ic.VM.Estack().PushVal(args[i]) + } + ic.VM.GasLimit = -1 +} + +func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Uint160, f callflag.CallFlag, args ...interface{}) { + ic.SpawnVM() + ic.VM.LoadScriptWithHash(script, hash, f) + for i := range args { + ic.VM.Estack().PushVal(args[i]) + } + ic.VM.GasLimit = -1 +} diff --git a/pkg/core/interop_system_core_test.go b/pkg/core/interop_system_core_test.go index 4c9f3ae73..d2a08cf76 100644 --- a/pkg/core/interop_system_core_test.go +++ b/pkg/core/interop_system_core_test.go @@ -13,7 +13,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/contract" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage" @@ -558,15 +557,6 @@ func createVMAndContractState(t testing.TB) (*vm.VM, *state.Contract, *interop.C return v, contractState, context, chain } -func loadScript(ic *interop.Context, script []byte, args ...interface{}) { - ic.SpawnVM() - ic.VM.LoadScriptWithFlags(script, callflag.AllowCall) - for i := range args { - ic.VM.Estack().PushVal(args[i]) - } - ic.VM.GasLimit = -1 -} - func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Uint160, f callflag.CallFlag, args ...interface{}) { ic.SpawnVM() ic.VM.LoadScriptWithHash(script, hash, f) @@ -576,136 +566,6 @@ func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Ui ic.VM.GasLimit = -1 } -func TestContractCall(t *testing.T) { - _, ic, _ := createVM(t) - - cs, currCs := contracts.GetTestContractState(t, pathToInternalContracts, 4, 5, random.Uint160()) // sender and IDs are not important for the test - require.NoError(t, native.PutContractState(ic.DAO, cs)) - require.NoError(t, native.PutContractState(ic.DAO, currCs)) - - currScript := currCs.NEF.Script - h := cs.Hash - - addArgs := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(2)}) - t.Run("Good", func(t *testing.T) { - t.Run("2 arguments", func(t *testing.T) { - loadScript(ic, currScript, 42) - ic.VM.Estack().PushVal(addArgs) - ic.VM.Estack().PushVal(callflag.All) - ic.VM.Estack().PushVal("add") - ic.VM.Estack().PushVal(h.BytesBE()) - require.NoError(t, contract.Call(ic)) - require.NoError(t, ic.VM.Run()) - require.Equal(t, 2, ic.VM.Estack().Len()) - require.Equal(t, big.NewInt(3), ic.VM.Estack().Pop().Value()) - require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value()) - }) - t.Run("3 arguments", func(t *testing.T) { - loadScript(ic, currScript, 42) - ic.VM.Estack().PushVal(stackitem.NewArray( - append(addArgs.Value().([]stackitem.Item), stackitem.Make(3)))) - ic.VM.Estack().PushVal(callflag.All) - ic.VM.Estack().PushVal("add") - ic.VM.Estack().PushVal(h.BytesBE()) - require.NoError(t, contract.Call(ic)) - require.NoError(t, ic.VM.Run()) - require.Equal(t, 2, ic.VM.Estack().Len()) - require.Equal(t, big.NewInt(6), ic.VM.Estack().Pop().Value()) - require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value()) - }) - }) - - t.Run("CallExInvalidFlag", func(t *testing.T) { - loadScript(ic, currScript, 42) - ic.VM.Estack().PushVal(addArgs) - ic.VM.Estack().PushVal(byte(0xFF)) - ic.VM.Estack().PushVal("add") - ic.VM.Estack().PushVal(h.BytesBE()) - require.Error(t, contract.Call(ic)) - }) - - runInvalid := func(args ...interface{}) func(t *testing.T) { - return func(t *testing.T) { - loadScriptWithHashAndFlags(ic, currScript, h, callflag.All, 42) - for i := range args { - ic.VM.Estack().PushVal(args[i]) - } - // interops can both return error and panic, - // we don't care which kind of error has occurred - require.Panics(t, func() { - err := contract.Call(ic) - if err != nil { - panic(err) - } - }) - } - } - - 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())) - t.Run("TooMuchArguments", runInvalid( - stackitem.NewArray([]stackitem.Item{ - stackitem.Make(1), stackitem.Make(2), stackitem.Make(3), stackitem.Make(4)}), - "add", h.BytesBE())) - }) - - t.Run("ReturnValues", func(t *testing.T) { - t.Run("Many", func(t *testing.T) { - loadScript(ic, currScript, 42) - ic.VM.Estack().PushVal(stackitem.NewArray(nil)) - ic.VM.Estack().PushVal(callflag.All) - ic.VM.Estack().PushVal("invalidReturn") - ic.VM.Estack().PushVal(h.BytesBE()) - require.NoError(t, contract.Call(ic)) - require.Error(t, ic.VM.Run()) - }) - t.Run("Void", func(t *testing.T) { - loadScript(ic, currScript, 42) - ic.VM.Estack().PushVal(stackitem.NewArray(nil)) - ic.VM.Estack().PushVal(callflag.All) - ic.VM.Estack().PushVal("justReturn") - ic.VM.Estack().PushVal(h.BytesBE()) - require.NoError(t, contract.Call(ic)) - require.NoError(t, ic.VM.Run()) - require.Equal(t, 2, ic.VM.Estack().Len()) - require.Equal(t, stackitem.Null{}, ic.VM.Estack().Pop().Item()) - require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value()) - }) - }) - - t.Run("IsolatedStack", func(t *testing.T) { - loadScript(ic, currScript, 42) - ic.VM.Estack().PushVal(stackitem.NewArray(nil)) - ic.VM.Estack().PushVal(callflag.All) - ic.VM.Estack().PushVal("drop") - ic.VM.Estack().PushVal(h.BytesBE()) - require.NoError(t, contract.Call(ic)) - require.Error(t, ic.VM.Run()) - }) - - t.Run("CallInitialize", func(t *testing.T) { - t.Run("Directly", runInvalid(stackitem.NewArray([]stackitem.Item{}), "_initialize", h.BytesBE())) - - loadScript(ic, currScript, 42) - ic.VM.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(5)})) - ic.VM.Estack().PushVal(callflag.All) - ic.VM.Estack().PushVal("add3") - ic.VM.Estack().PushVal(h.BytesBE()) - require.NoError(t, contract.Call(ic)) - require.NoError(t, ic.VM.Run()) - require.Equal(t, 2, ic.VM.Estack().Len()) - require.Equal(t, big.NewInt(8), ic.VM.Estack().Pop().Value()) - require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value()) - }) -} - func TestRuntimeCheckWitness(t *testing.T) { _, ic, _ := createVM(t)