2020-08-21 18:36:08 +00:00
|
|
|
package smartcontract
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2020-08-26 09:08:17 +00:00
|
|
|
"strings"
|
2020-08-21 18:36:08 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/urfave/cli"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestInitSmartContract(t *testing.T) {
|
|
|
|
d, err := ioutil.TempDir("./", "")
|
|
|
|
require.NoError(t, err)
|
|
|
|
os.Chdir(d)
|
|
|
|
defer func() {
|
|
|
|
os.Chdir("..")
|
|
|
|
os.RemoveAll(d)
|
|
|
|
}()
|
|
|
|
contractName := "testContract"
|
|
|
|
|
|
|
|
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
|
|
|
|
set.String("name", contractName, "")
|
|
|
|
ctx := cli.NewContext(cli.NewApp(), set, nil)
|
|
|
|
require.NoError(t, initSmartContract(ctx))
|
|
|
|
dirInfo, err := os.Stat(contractName)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, dirInfo.IsDir())
|
|
|
|
files, err := ioutil.ReadDir(contractName)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 2, len(files))
|
|
|
|
require.Equal(t, "main.go", files[0].Name())
|
|
|
|
require.Equal(t, "neo-go.yml", files[1].Name())
|
|
|
|
main, err := ioutil.ReadFile(contractName + "/" + files[0].Name())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t,
|
|
|
|
`package `+contractName+`
|
|
|
|
|
|
|
|
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
|
|
|
|
|
|
|
var notificationName string
|
|
|
|
|
|
|
|
// init initializes notificationName before calling any other smart-contract method
|
|
|
|
func init() {
|
|
|
|
notificationName = "Hello world!"
|
|
|
|
}
|
|
|
|
|
|
|
|
// RuntimeNotify sends runtime notification with "Hello world!" name
|
|
|
|
func RuntimeNotify(args []interface{}) {
|
|
|
|
runtime.Notify(notificationName, args)
|
|
|
|
}`, string(main))
|
|
|
|
|
|
|
|
manifest, err := ioutil.ReadFile(contractName + "/" + files[1].Name())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t,
|
|
|
|
`hasstorage: false
|
|
|
|
ispayable: false
|
|
|
|
supportedstandards: []
|
|
|
|
events:
|
|
|
|
- name: Hello world!
|
|
|
|
parameters:
|
|
|
|
- name: args
|
|
|
|
type: Array
|
|
|
|
`, string(manifest))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetFeatures(t *testing.T) {
|
|
|
|
cfg := ProjectConfig{
|
|
|
|
IsPayable: true,
|
|
|
|
HasStorage: true,
|
|
|
|
}
|
|
|
|
f := cfg.GetFeatures()
|
|
|
|
require.Equal(t, smartcontract.IsPayable|smartcontract.HasStorage, f)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseCosigner(t *testing.T) {
|
|
|
|
acc := util.Uint160{1, 3, 5, 7}
|
|
|
|
testCases := map[string]transaction.Signer{
|
|
|
|
acc.StringLE(): {
|
|
|
|
Account: acc,
|
|
|
|
Scopes: transaction.Global,
|
|
|
|
},
|
|
|
|
"0x" + acc.StringLE(): {
|
|
|
|
Account: acc,
|
|
|
|
Scopes: transaction.Global,
|
|
|
|
},
|
|
|
|
acc.StringLE() + ":Global": {
|
|
|
|
Account: acc,
|
|
|
|
Scopes: transaction.Global,
|
|
|
|
},
|
|
|
|
acc.StringLE() + ":CalledByEntry": {
|
|
|
|
Account: acc,
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
},
|
|
|
|
acc.StringLE() + ":FeeOnly": {
|
|
|
|
Account: acc,
|
|
|
|
Scopes: transaction.FeeOnly,
|
|
|
|
},
|
|
|
|
acc.StringLE() + ":CalledByEntry,CustomContracts": {
|
|
|
|
Account: acc,
|
|
|
|
Scopes: transaction.CalledByEntry | transaction.CustomContracts,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for s, expected := range testCases {
|
|
|
|
actual, err := parseCosigner(s)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expected, actual)
|
|
|
|
}
|
|
|
|
errorCases := []string{
|
|
|
|
acc.StringLE() + "0",
|
|
|
|
acc.StringLE() + ":Unknown",
|
|
|
|
acc.StringLE() + ":Global,CustomContracts",
|
|
|
|
acc.StringLE() + ":Global,FeeOnly",
|
|
|
|
}
|
|
|
|
for _, s := range errorCases {
|
|
|
|
_, err := parseCosigner(s)
|
|
|
|
require.Error(t, err)
|
|
|
|
}
|
|
|
|
}
|
2020-08-26 09:08:17 +00:00
|
|
|
|
|
|
|
func TestParseParams_CalledFromItself(t *testing.T) {
|
|
|
|
testCases := map[string]struct {
|
|
|
|
WordsRead int
|
|
|
|
Value []smartcontract.Parameter
|
|
|
|
}{
|
|
|
|
"]": {
|
|
|
|
WordsRead: 1,
|
|
|
|
Value: []smartcontract.Parameter{},
|
|
|
|
},
|
|
|
|
"[ [ ] ] ]": {
|
|
|
|
WordsRead: 5,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"a b c ]": {
|
|
|
|
WordsRead: 4,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "b",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "c",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"a [ b [ [ c d ] e ] ] f ] extra items": {
|
|
|
|
WordsRead: 13, // the method should return right after the last bracket, as calledFromMain == false
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "b",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "c",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "d",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "e",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "f",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for str, expected := range testCases {
|
|
|
|
input := strings.Split(str, " ")
|
|
|
|
offset, actual, err := parseParams(input, false)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expected.WordsRead, offset)
|
|
|
|
require.Equal(t, expected.Value, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
errorCases := []string{
|
|
|
|
"[ ]",
|
|
|
|
"[ a b [ c ] d ]",
|
|
|
|
"[ ] --",
|
|
|
|
"--",
|
|
|
|
"not-int:integer ]",
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, str := range errorCases {
|
|
|
|
input := strings.Split(str, " ")
|
|
|
|
_, _, err := parseParams(input, false)
|
|
|
|
require.Error(t, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseParams_CalledFromOutside(t *testing.T) {
|
|
|
|
testCases := map[string]struct {
|
|
|
|
WordsRead int
|
|
|
|
Parameters []smartcontract.Parameter
|
|
|
|
}{
|
|
|
|
"-- cosigner1": {
|
|
|
|
WordsRead: 1, // the `--` only
|
|
|
|
Parameters: []smartcontract.Parameter{},
|
|
|
|
},
|
|
|
|
"a b c": {
|
|
|
|
WordsRead: 3,
|
|
|
|
Parameters: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "b",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "c",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"a b c -- cosigner1": {
|
|
|
|
WordsRead: 4,
|
|
|
|
Parameters: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "b",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "c",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"a [ b [ [ c d ] e ] ] f": {
|
|
|
|
WordsRead: 12,
|
|
|
|
Parameters: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "b",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "c",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "d",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "e",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "f",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"a [ b ] -- cosigner1 cosigner2": {
|
|
|
|
WordsRead: 5,
|
|
|
|
Parameters: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "b",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"a [ b ]": {
|
|
|
|
WordsRead: 4,
|
|
|
|
Parameters: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "b",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"a [ b ] [ [ c ] ] [ [ [ d ] ] ]": {
|
|
|
|
WordsRead: 16,
|
|
|
|
Parameters: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "b",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "c",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.ArrayType,
|
|
|
|
Value: []smartcontract.Parameter{
|
|
|
|
{
|
|
|
|
Type: smartcontract.StringType,
|
|
|
|
Value: "d",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for str, expected := range testCases {
|
|
|
|
input := strings.Split(str, " ")
|
|
|
|
offset, arr, err := parseParams(input, true)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expected.WordsRead, offset)
|
|
|
|
require.Equal(t, expected.Parameters, arr)
|
|
|
|
}
|
|
|
|
|
|
|
|
errorCases := []string{
|
|
|
|
"[",
|
|
|
|
"]",
|
|
|
|
"[ [ ]",
|
|
|
|
"[ [ ] --",
|
|
|
|
"[ -- ]",
|
|
|
|
}
|
|
|
|
for _, str := range errorCases {
|
|
|
|
input := strings.Split(str, " ")
|
|
|
|
_, _, err := parseParams(input, true)
|
|
|
|
require.Error(t, err)
|
|
|
|
}
|
|
|
|
}
|