Merge pull request #1809 from nspcc-dev/fix/syscalltest
compiler: add test for syscall handling
This commit is contained in:
commit
1d70504003
3 changed files with 135 additions and 2 deletions
|
@ -1,9 +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/core"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||
istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
|
@ -37,6 +43,133 @@ func TestFindFlags(t *testing.T) {
|
|||
require.EqualValues(t, storage.PickField1, istorage.FindPick1)
|
||||
}
|
||||
|
||||
type syscallTestCase struct {
|
||||
method string
|
||||
params []string
|
||||
isVoid bool
|
||||
}
|
||||
|
||||
// This test ensures that our wrappers have necessary number of parameters
|
||||
// and execute needed syscall. Because of lack of typing (compared to native contracts)
|
||||
// parameter types can't be checked.
|
||||
func TestSyscallExecution(t *testing.T) {
|
||||
b := `[]byte{1}`
|
||||
u160 := `interop.Hash160("aaaaaaaaaaaaaaaaaaaa")`
|
||||
pub := `interop.PublicKey("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")`
|
||||
pubs := "[]interop.PublicKey{ " + pub + "}"
|
||||
sig := `interop.Signature("aaaaaa")`
|
||||
sigs := "[]interop.Signature{" + sig + "}"
|
||||
sctx := "storage.Context{}"
|
||||
interops := map[string]syscallTestCase{
|
||||
"binary.Atoi": {interopnames.SystemBinaryAtoi, []string{`"123"`, "10"}, false},
|
||||
"binary.Base58Decode": {interopnames.SystemBinaryBase58Decode, []string{b}, false},
|
||||
"binary.Base58Encode": {interopnames.SystemBinaryBase58Encode, []string{b}, false},
|
||||
"binary.Base64Decode": {interopnames.SystemBinaryBase64Decode, []string{b}, false},
|
||||
"binary.Base64Encode": {interopnames.SystemBinaryBase64Encode, []string{b}, false},
|
||||
"binary.Deserialize": {interopnames.SystemBinaryDeserialize, []string{b}, false},
|
||||
"binary.Itoa": {interopnames.SystemBinaryItoa, []string{"123", "10"}, false},
|
||||
"binary.Serialize": {interopnames.SystemBinarySerialize, []string{"10"}, false},
|
||||
"contract.Call": {interopnames.SystemContractCall, []string{u160, `"m"`, "1", "3"}, false},
|
||||
"contract.CreateMultisigAccount": {interopnames.SystemContractCreateMultisigAccount, []string{"1", pubs}, false},
|
||||
"contract.CreateStandardAccount": {interopnames.SystemContractCreateStandardAccount, []string{pub}, false},
|
||||
"contract.IsStandard": {interopnames.SystemContractIsStandard, []string{b}, false},
|
||||
"contract.GetCallFlags": {interopnames.SystemContractGetCallFlags, nil, false},
|
||||
"iterator.Create": {interopnames.SystemIteratorCreate, []string{pubs}, false},
|
||||
"iterator.Next": {interopnames.SystemIteratorNext, []string{"iterator.Iterator{}"}, false},
|
||||
"iterator.Value": {interopnames.SystemIteratorValue, []string{"iterator.Iterator{}"}, false},
|
||||
"json.FromJSON": {interopnames.SystemJSONDeserialize, []string{b}, false},
|
||||
"json.ToJSON": {interopnames.SystemJSONSerialize, []string{b}, false},
|
||||
"runtime.CheckWitness": {interopnames.SystemRuntimeCheckWitness, []string{b}, false},
|
||||
"runtime.GasLeft": {interopnames.SystemRuntimeGasLeft, nil, false},
|
||||
"runtime.GetCallingScriptHash": {interopnames.SystemRuntimeGetCallingScriptHash, nil, false},
|
||||
"runtime.GetEntryScriptHash": {interopnames.SystemRuntimeGetEntryScriptHash, nil, false},
|
||||
"runtime.GetExecutingScriptHash": {interopnames.SystemRuntimeGetExecutingScriptHash, nil, false},
|
||||
"runtime.GetInvocationCounter": {interopnames.SystemRuntimeGetInvocationCounter, nil, false},
|
||||
"runtime.GetNotifications": {interopnames.SystemRuntimeGetNotifications, []string{u160}, false},
|
||||
"runtime.GetScriptContainer": {interopnames.SystemRuntimeGetScriptContainer, nil, false},
|
||||
"runtime.GetTime": {interopnames.SystemRuntimeGetTime, nil, false},
|
||||
"runtime.GetTrigger": {interopnames.SystemRuntimeGetTrigger, nil, false},
|
||||
"runtime.Log": {interopnames.SystemRuntimeLog, []string{`"msg"`}, true},
|
||||
"runtime.Notify": {interopnames.SystemRuntimeNotify, []string{`"ev"`, "1"}, true},
|
||||
"runtime.Platform": {interopnames.SystemRuntimePlatform, nil, false},
|
||||
"storage.Delete": {interopnames.SystemStorageDelete, []string{sctx, b}, true},
|
||||
"storage.Find": {interopnames.SystemStorageFind, []string{sctx, b, "storage.None"}, false},
|
||||
"storage.Get": {interopnames.SystemStorageGet, []string{sctx, b}, false},
|
||||
"storage.GetContext": {interopnames.SystemStorageGetContext, nil, false},
|
||||
"storage.GetReadOnlyContext": {interopnames.SystemStorageGetReadOnlyContext, nil, false},
|
||||
"storage.Put": {interopnames.SystemStoragePut, []string{sctx, b, b}, true},
|
||||
"storage.PutEx": {interopnames.SystemStoragePutEx, []string{sctx, b, b, "storage.PutConstant"}, true},
|
||||
"storage.ConvertContextToReadOnly": {interopnames.SystemStorageAsReadOnly, []string{sctx}, false},
|
||||
"crypto.ECDsaSecp256r1Verify": {interopnames.NeoCryptoVerifyWithECDsaSecp256r1, []string{b, pub, sig}, false},
|
||||
"crypto.ECDsaSecp256k1Verify": {interopnames.NeoCryptoVerifyWithECDsaSecp256k1, []string{b, pub, sig}, false},
|
||||
"crypto.ECDSASecp256r1CheckMultisig": {interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, []string{b, pubs, sigs}, false},
|
||||
"crypto.ECDSASecp256k1CheckMultisig": {interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, []string{b, pubs, sigs}, false},
|
||||
"crypto.SHA256": {interopnames.NeoCryptoSHA256, []string{b}, false},
|
||||
"crypto.RIPEMD160": {interopnames.NeoCryptoRIPEMD160, []string{b}, false},
|
||||
}
|
||||
ic := &interop.Context{}
|
||||
core.SpawnVM(ic) // set Functions field
|
||||
for _, fs := range ic.Functions {
|
||||
for i := range fs {
|
||||
// It will be set in test and we want to fail if calling invalid syscall.
|
||||
fs[i].Func = nil
|
||||
}
|
||||
}
|
||||
for goName, tc := range interops {
|
||||
t.Run(goName, func(t *testing.T) {
|
||||
runSyscallTestCase(t, ic, goName, tc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func runSyscallTestCase(t *testing.T, ic *interop.Context, goName string, tc syscallTestCase) {
|
||||
syscallID := interopnames.ToID([]byte(tc.method))
|
||||
f := ic.GetFunction(syscallID)
|
||||
require.NotNil(t, f)
|
||||
require.Equal(t, f.ParamCount, len(tc.params))
|
||||
called := false
|
||||
f.Func = func(ic *interop.Context) error {
|
||||
called = true
|
||||
if ic.VM.Estack().Len() < f.ParamCount {
|
||||
return errors.New("not enough parameters")
|
||||
}
|
||||
for i := 0; i < f.ParamCount; i++ {
|
||||
ic.VM.Estack().Pop()
|
||||
}
|
||||
if !tc.isVoid {
|
||||
ic.VM.Estack().PushVal(42)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
defer func() { f.Func = nil }()
|
||||
|
||||
srcTmpl := `package foo
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/%s"
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
func unused() { var _ interop.Hash160 }
|
||||
`
|
||||
if tc.isVoid {
|
||||
srcTmpl += `func Main() { %s(%s) }`
|
||||
} else {
|
||||
srcTmpl += `func Main() interface{} { return %s(%s) }`
|
||||
}
|
||||
ss := strings.Split(goName, ".")
|
||||
src := fmt.Sprintf(srcTmpl, ss[0], goName, strings.Join(tc.params, ", "))
|
||||
b, _, err := compiler.CompileWithDebugInfo("foo", strings.NewReader(src))
|
||||
require.NoError(t, err)
|
||||
|
||||
v := ic.SpawnVM()
|
||||
v.LoadScriptWithFlags(b, callflag.All)
|
||||
require.NoError(t, v.Run())
|
||||
require.True(t, called)
|
||||
if tc.isVoid {
|
||||
require.Equal(t, 0, v.Estack().Len())
|
||||
} else {
|
||||
require.Equal(t, 1, v.Estack().Len())
|
||||
require.Equal(t, big.NewInt(42), v.Estack().Pop().Value())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoragePutGet(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
|
|
|
@ -73,7 +73,7 @@ var systemInterops = []interop.Function{
|
|||
{Name: interopnames.SystemStorageDelete, Func: storageDelete, Price: 0,
|
||||
RequiredFlags: callflag.WriteStates, ParamCount: 2},
|
||||
{Name: interopnames.SystemStorageFind, Func: storageFind, Price: 1 << 15, RequiredFlags: callflag.ReadStates,
|
||||
ParamCount: 2},
|
||||
ParamCount: 3},
|
||||
{Name: interopnames.SystemStorageGet, Func: storageGet, Price: 1 << 15, RequiredFlags: callflag.ReadStates,
|
||||
ParamCount: 2},
|
||||
{Name: interopnames.SystemStorageGetContext, Func: storageGetContext, Price: 1 << 4,
|
||||
|
|
|
@ -46,7 +46,7 @@ func CreateStandardAccount(pub interop.PublicKey) []byte {
|
|||
// GetCallFlags returns calling flags which execution context was created with.
|
||||
// This function uses `System.Contract.GetCallFlags` syscall.
|
||||
func GetCallFlags() CallFlag {
|
||||
return neogointernal.Syscall0("System.Contract.GetFlags").(CallFlag)
|
||||
return neogointernal.Syscall0("System.Contract.GetCallFlags").(CallFlag)
|
||||
}
|
||||
|
||||
// Call executes previously deployed blockchain contract with specified hash
|
||||
|
|
Loading…
Reference in a new issue