mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-25 23:42:23 +00:00
cli: unify parameters parsing
Share parameters parsing code between 'contract invokefunction' and 'vm run' commands. It allows VM CLI to parse more complicated parameter types including arrays and file-backed bytestrings.
This commit is contained in:
parent
3fba4e4f17
commit
7eb87afab8
8 changed files with 167 additions and 147 deletions
|
@ -24,6 +24,76 @@ const (
|
||||||
ArrayEndSeparator = "]"
|
ArrayEndSeparator = "]"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ParamsParsingDoc is a documentation for parameters parsing.
|
||||||
|
ParamsParsingDoc = ` Arguments always do have regular Neo smart contract parameter types, either
|
||||||
|
specified explicitly or being inferred from the value. To specify the type
|
||||||
|
manually use "type:value" syntax where the type is one of the following:
|
||||||
|
'signature', 'bool', 'int', 'hash160', 'hash256', 'bytes', 'key' or 'string'.
|
||||||
|
Array types are also supported: use special space-separated '[' and ']'
|
||||||
|
symbols around array values to denote array bounds. Nested arrays are also
|
||||||
|
supported.
|
||||||
|
|
||||||
|
There is ability to provide an argument of 'bytearray' type via file. Use a
|
||||||
|
special 'filebytes' argument type for this with a filepath specified after
|
||||||
|
the colon, e.g. 'filebytes:my_file.txt'.
|
||||||
|
|
||||||
|
Given values are type-checked against given types with the following
|
||||||
|
restrictions applied:
|
||||||
|
* 'signature' type values should be hex-encoded and have a (decoded)
|
||||||
|
length of 64 bytes.
|
||||||
|
* 'bool' type values are 'true' and 'false'.
|
||||||
|
* 'int' values are decimal integers that can be successfully converted
|
||||||
|
from the string.
|
||||||
|
* 'hash160' values are Neo addresses and hex-encoded 20-bytes long (after
|
||||||
|
decoding) strings.
|
||||||
|
* 'hash256' type values should be hex-encoded and have a (decoded)
|
||||||
|
length of 32 bytes.
|
||||||
|
* 'bytes' type values are any hex-encoded things.
|
||||||
|
* 'filebytes' type values are filenames with the argument value inside.
|
||||||
|
* 'key' type values are hex-encoded marshalled public keys.
|
||||||
|
* 'string' type values are any valid UTF-8 strings. In the value's part of
|
||||||
|
the string the colon looses it's special meaning as a separator between
|
||||||
|
type and value and is taken literally.
|
||||||
|
|
||||||
|
If no type is explicitly specified, it is inferred from the value using the
|
||||||
|
following logic:
|
||||||
|
- anything that can be interpreted as a decimal integer gets
|
||||||
|
an 'int' type
|
||||||
|
- 'true' and 'false' strings get 'bool' type
|
||||||
|
- valid Neo addresses and 20 bytes long hex-encoded strings get 'hash160'
|
||||||
|
type
|
||||||
|
- valid hex-encoded public keys get 'key' type
|
||||||
|
- 32 bytes long hex-encoded values get 'hash256' type
|
||||||
|
- 64 bytes long hex-encoded values get 'signature' type
|
||||||
|
- any other valid hex-encoded values get 'bytes' type
|
||||||
|
- anything else is a 'string'
|
||||||
|
|
||||||
|
Backslash character is used as an escape character and allows to use colon in
|
||||||
|
an implicitly typed string. For any other characters it has no special
|
||||||
|
meaning, to get a literal backslash in the string use the '\\' sequence.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
* 'int:42' is an integer with a value of 42
|
||||||
|
* '42' is an integer with a value of 42
|
||||||
|
* 'bad' is a string with a value of 'bad'
|
||||||
|
* 'dead' is a byte array with a value of 'dead'
|
||||||
|
* 'string:dead' is a string with a value of 'dead'
|
||||||
|
* 'filebytes:my_data.txt' is bytes decoded from a content of my_data.txt
|
||||||
|
* 'AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y' is a hash160 with a value
|
||||||
|
of '23ba2703c53263e8d6e522dc32203339dcd8eee9'
|
||||||
|
* '\4\2' is an integer with a value of 42
|
||||||
|
* '\\4\2' is a string with a value of '\42'
|
||||||
|
* 'string:string' is a string with a value of 'string'
|
||||||
|
* 'string\:string' is a string with a value of 'string:string'
|
||||||
|
* '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a
|
||||||
|
key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c'
|
||||||
|
* '[ a b c ]' is an array with strings values 'a', 'b' and 'c'
|
||||||
|
* '[ a b [ c d ] e ]' is an array with 4 values: string 'a', string 'b',
|
||||||
|
array of two strings 'c' and 'd', string 'e'
|
||||||
|
* '[ ]' is an empty array`
|
||||||
|
)
|
||||||
|
|
||||||
// GetSignersFromContext returns signers parsed from context args starting
|
// GetSignersFromContext returns signers parsed from context args starting
|
||||||
// from the specified offset.
|
// from the specified offset.
|
||||||
func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, *cli.ExitError) {
|
func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, *cli.ExitError) {
|
||||||
|
|
|
@ -209,72 +209,7 @@ func NewCommands() []cli.Command {
|
||||||
follow the regular convention of smart contract arguments (method string and
|
follow the regular convention of smart contract arguments (method string and
|
||||||
an array of other arguments).
|
an array of other arguments).
|
||||||
|
|
||||||
Arguments always do have regular Neo smart contract parameter types, either
|
` + cmdargs.ParamsParsingDoc + `
|
||||||
specified explicitly or being inferred from the value. To specify the type
|
|
||||||
manually use "type:value" syntax where the type is one of the following:
|
|
||||||
'signature', 'bool', 'int', 'hash160', 'hash256', 'bytes', 'key' or 'string'.
|
|
||||||
Array types are also supported: use special space-separated '[' and ']'
|
|
||||||
symbols around array values to denote array bounds. Nested arrays are also
|
|
||||||
supported.
|
|
||||||
|
|
||||||
There is ability to provide an argument of 'bytearray' type via file. Use a
|
|
||||||
special 'filebytes' argument type for this with a filepath specified after
|
|
||||||
the colon, e.g. 'filebytes:my_file.txt'.
|
|
||||||
|
|
||||||
Given values are type-checked against given types with the following
|
|
||||||
restrictions applied:
|
|
||||||
* 'signature' type values should be hex-encoded and have a (decoded)
|
|
||||||
length of 64 bytes.
|
|
||||||
* 'bool' type values are 'true' and 'false'.
|
|
||||||
* 'int' values are decimal integers that can be successfully converted
|
|
||||||
from the string.
|
|
||||||
* 'hash160' values are Neo addresses and hex-encoded 20-bytes long (after
|
|
||||||
decoding) strings.
|
|
||||||
* 'hash256' type values should be hex-encoded and have a (decoded)
|
|
||||||
length of 32 bytes.
|
|
||||||
* 'bytes' type values are any hex-encoded things.
|
|
||||||
* 'filebytes' type values are filenames with the argument value inside.
|
|
||||||
* 'key' type values are hex-encoded marshalled public keys.
|
|
||||||
* 'string' type values are any valid UTF-8 strings. In the value's part of
|
|
||||||
the string the colon looses it's special meaning as a separator between
|
|
||||||
type and value and is taken literally.
|
|
||||||
|
|
||||||
If no type is explicitly specified, it is inferred from the value using the
|
|
||||||
following logic:
|
|
||||||
- anything that can be interpreted as a decimal integer gets
|
|
||||||
an 'int' type
|
|
||||||
- 'true' and 'false' strings get 'bool' type
|
|
||||||
- valid Neo addresses and 20 bytes long hex-encoded strings get 'hash160'
|
|
||||||
type
|
|
||||||
- valid hex-encoded public keys get 'key' type
|
|
||||||
- 32 bytes long hex-encoded values get 'hash256' type
|
|
||||||
- 64 bytes long hex-encoded values get 'signature' type
|
|
||||||
- any other valid hex-encoded values get 'bytes' type
|
|
||||||
- anything else is a 'string'
|
|
||||||
|
|
||||||
Backslash character is used as an escape character and allows to use colon in
|
|
||||||
an implicitly typed string. For any other characters it has no special
|
|
||||||
meaning, to get a literal backslash in the string use the '\\' sequence.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
* 'int:42' is an integer with a value of 42
|
|
||||||
* '42' is an integer with a value of 42
|
|
||||||
* 'bad' is a string with a value of 'bad'
|
|
||||||
* 'dead' is a byte array with a value of 'dead'
|
|
||||||
* 'string:dead' is a string with a value of 'dead'
|
|
||||||
* 'filebytes:my_data.txt' is bytes decoded from a content of my_data.txt
|
|
||||||
* 'AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y' is a hash160 with a value
|
|
||||||
of '23ba2703c53263e8d6e522dc32203339dcd8eee9'
|
|
||||||
* '\4\2' is an integer with a value of 42
|
|
||||||
* '\\4\2' is a string with a value of '\42'
|
|
||||||
* 'string:string' is a string with a value of 'string'
|
|
||||||
* 'string\:string' is a string with a value of 'string:string'
|
|
||||||
* '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a
|
|
||||||
key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c'
|
|
||||||
* '[ a b c ]' is an array with strings values 'a', 'b' and 'c'
|
|
||||||
* '[ a b [ c d ] e ]' is an array with 4 values: string 'a', string 'b',
|
|
||||||
array of two strings 'c' and 'd', string 'e'
|
|
||||||
* '[ ]' is an empty array
|
|
||||||
|
|
||||||
Signers represent a set of Uint160 hashes with witness scopes and are used
|
Signers represent a set of Uint160 hashes with witness scopes and are used
|
||||||
to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated
|
to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
|
|
||||||
"github.com/chzyer/readline"
|
"github.com/chzyer/readline"
|
||||||
"github.com/kballard/go-shellquote"
|
"github.com/kballard/go-shellquote"
|
||||||
|
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
||||||
"github.com/nspcc-dev/neo-go/cli/flags"
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||||
"github.com/nspcc-dev/neo-go/cli/options"
|
"github.com/nspcc-dev/neo-go/cli/options"
|
||||||
"github.com/nspcc-dev/neo-go/cli/paramcontext"
|
"github.com/nspcc-dev/neo-go/cli/paramcontext"
|
||||||
|
@ -52,11 +53,6 @@ const (
|
||||||
exitFuncKey = "exitFunc"
|
exitFuncKey = "exitFunc"
|
||||||
readlineInstanceKey = "readlineKey"
|
readlineInstanceKey = "readlineKey"
|
||||||
printLogoKey = "printLogoKey"
|
printLogoKey = "printLogoKey"
|
||||||
boolType = "bool"
|
|
||||||
boolFalse = "false"
|
|
||||||
boolTrue = "true"
|
|
||||||
intType = "int"
|
|
||||||
stringType = "string"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Various flag names.
|
// Various flag names.
|
||||||
|
@ -221,18 +217,12 @@ and converted to other formats. Strings are escaped and output in quotes.`,
|
||||||
<method> is a contract method, specified in manifest. It can be '_' which will push
|
<method> is a contract method, specified in manifest. It can be '_' which will push
|
||||||
parameters onto the stack and execute from the current offset.
|
parameters onto the stack and execute from the current offset.
|
||||||
<parameter> is a parameter (can be repeated multiple times) that can be specified
|
<parameter> is a parameter (can be repeated multiple times) that can be specified
|
||||||
as <type>:<value>, where type can be:
|
using the same rules as for 'contract testinvokefunction' command:
|
||||||
'` + boolType + `': supports '` + boolFalse + `' and '` + boolTrue + `' values
|
|
||||||
'` + intType + `': supports integers as values
|
` + cmdargs.ParamsParsingDoc + `
|
||||||
'` + stringType + `': supports strings as values (that are pushed as a byte array
|
|
||||||
values to the stack)
|
|
||||||
or can be just <value>, for which the type will be detected automatically
|
|
||||||
following these rules: '` + boolTrue + `' and '` + boolFalse + `' are treated as respective
|
|
||||||
boolean values, everything that can be converted to integer is treated as
|
|
||||||
integer and everything else is treated like a string.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
> run put ` + stringType + `:"Something to put"`,
|
> run put string:"Something to put"`,
|
||||||
Action: handleRun,
|
Action: handleRun,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -862,9 +852,16 @@ func handleRun(c *cli.Context) error {
|
||||||
runCurrent = args[0] != "_"
|
runCurrent = args[0] != "_"
|
||||||
)
|
)
|
||||||
|
|
||||||
params, err = parseArgs(args[1:])
|
_, scParams, err := cmdargs.ParseParams(args[1:], true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %v", ErrInvalidParameter, err)
|
||||||
|
}
|
||||||
|
params = make([]stackitem.Item, len(scParams))
|
||||||
|
for i := range scParams {
|
||||||
|
params[i], err = scParams[i].ToStackItem()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to convert parameter #%d to stackitem: %w", i, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if runCurrent {
|
if runCurrent {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
|
@ -1265,48 +1262,6 @@ func Parse(args []string) (string, error) {
|
||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseArgs(args []string) ([]stackitem.Item, error) {
|
|
||||||
items := make([]stackitem.Item, len(args))
|
|
||||||
for i, arg := range args {
|
|
||||||
var typ, value string
|
|
||||||
typeAndVal := strings.Split(arg, ":")
|
|
||||||
if len(typeAndVal) < 2 {
|
|
||||||
if typeAndVal[0] == boolFalse || typeAndVal[0] == boolTrue {
|
|
||||||
typ = boolType
|
|
||||||
} else if _, err := strconv.Atoi(typeAndVal[0]); err == nil {
|
|
||||||
typ = intType
|
|
||||||
} else {
|
|
||||||
typ = stringType
|
|
||||||
}
|
|
||||||
value = typeAndVal[0]
|
|
||||||
} else {
|
|
||||||
typ = typeAndVal[0]
|
|
||||||
value = typeAndVal[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
switch typ {
|
|
||||||
case boolType:
|
|
||||||
if value == boolFalse {
|
|
||||||
items[i] = stackitem.NewBool(false)
|
|
||||||
} else if value == boolTrue {
|
|
||||||
items[i] = stackitem.NewBool(true)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("%w: invalid bool value", ErrInvalidParameter)
|
|
||||||
}
|
|
||||||
case intType:
|
|
||||||
val, err := strconv.ParseInt(value, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("%w: invalid integer value", ErrInvalidParameter)
|
|
||||||
}
|
|
||||||
items[i] = stackitem.NewBigInteger(big.NewInt(val))
|
|
||||||
case stringType:
|
|
||||||
items[i] = stackitem.NewByteArray([]byte(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return items, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const logo = `
|
const logo = `
|
||||||
_ ____________ __________ _ ____ ___
|
_ ____________ __________ _ ____ ___
|
||||||
/ | / / ____/ __ \ / ____/ __ \ | | / / |/ /
|
/ | / / ____/ __ \ / ____/ __ \ | | / / |/ /
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
gio "io"
|
gio "io"
|
||||||
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -428,6 +429,9 @@ func TestRunWithDifferentArguments(t *testing.T) {
|
||||||
}
|
}
|
||||||
func GetString(arg string) string {
|
func GetString(arg string) string {
|
||||||
return arg
|
return arg
|
||||||
|
}
|
||||||
|
func GetArr(arg []interface{}) []interface{}{
|
||||||
|
return arg
|
||||||
}`
|
}`
|
||||||
|
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
@ -449,6 +453,7 @@ func TestRunWithDifferentArguments(t *testing.T) {
|
||||||
"run _ 1 2",
|
"run _ 1 2",
|
||||||
"loadbase64 "+base64.StdEncoding.EncodeToString([]byte{byte(opcode.MUL)}),
|
"loadbase64 "+base64.StdEncoding.EncodeToString([]byte{byte(opcode.MUL)}),
|
||||||
"run _ 21 2",
|
"run _ 21 2",
|
||||||
|
"loadgo "+filename, "run getArr [ 1 2 3 ]",
|
||||||
)
|
)
|
||||||
|
|
||||||
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
||||||
|
@ -480,6 +485,13 @@ func TestRunWithDifferentArguments(t *testing.T) {
|
||||||
|
|
||||||
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
||||||
e.checkStack(t, 42)
|
e.checkStack(t, 42)
|
||||||
|
|
||||||
|
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
||||||
|
e.checkStack(t, []stackitem.Item{
|
||||||
|
stackitem.NewBigInteger(big.NewInt(1)),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(2)),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(3)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrintOps(t *testing.T) {
|
func TestPrintOps(t *testing.T) {
|
||||||
|
|
|
@ -403,3 +403,12 @@ func ExpandParameterToEmitable(param Parameter) (interface{}, error) {
|
||||||
return param.Value, nil
|
return param.Value, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToStackItem converts smartcontract parameter to stackitem.Item.
|
||||||
|
func (p *Parameter) ToStackItem() (stackitem.Item, error) {
|
||||||
|
e, err := ExpandParameterToEmitable(*p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return stackitem.Make(e), nil
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -446,47 +448,57 @@ func hexToBase64(s string) string {
|
||||||
return base64.StdEncoding.EncodeToString(b)
|
return base64.StdEncoding.EncodeToString(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExpandParameterToEmitable(t *testing.T) {
|
func TestExpandParameterToEmitableToStackitem(t *testing.T) {
|
||||||
pk, _ := keys.NewPrivateKey()
|
pk, _ := keys.NewPrivateKey()
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
In Parameter
|
In Parameter
|
||||||
Expected interface{}
|
Expected interface{}
|
||||||
|
ExpectedStackitem stackitem.Item
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
In: Parameter{Type: BoolType, Value: true},
|
In: Parameter{Type: BoolType, Value: true},
|
||||||
Expected: true,
|
Expected: true,
|
||||||
|
ExpectedStackitem: stackitem.NewBool(true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: Parameter{Type: IntegerType, Value: big.NewInt(123)},
|
In: Parameter{Type: IntegerType, Value: big.NewInt(123)},
|
||||||
Expected: big.NewInt(123),
|
Expected: big.NewInt(123),
|
||||||
|
ExpectedStackitem: stackitem.NewBigInteger(big.NewInt(123)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: Parameter{Type: ByteArrayType, Value: []byte{1, 2, 3}},
|
In: Parameter{Type: ByteArrayType, Value: []byte{1, 2, 3}},
|
||||||
Expected: []byte{1, 2, 3},
|
Expected: []byte{1, 2, 3},
|
||||||
|
ExpectedStackitem: stackitem.NewByteArray([]byte{1, 2, 3}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: Parameter{Type: StringType, Value: "writing's on the wall"},
|
In: Parameter{Type: StringType, Value: "writing's on the wall"},
|
||||||
Expected: "writing's on the wall",
|
Expected: "writing's on the wall",
|
||||||
|
ExpectedStackitem: stackitem.NewByteArray([]byte("writing's on the wall")),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: Parameter{Type: Hash160Type, Value: util.Uint160{1, 2, 3}},
|
In: Parameter{Type: Hash160Type, Value: util.Uint160{1, 2, 3}},
|
||||||
Expected: util.Uint160{1, 2, 3},
|
Expected: util.Uint160{1, 2, 3},
|
||||||
|
ExpectedStackitem: stackitem.NewByteArray(util.Uint160{1, 2, 3}.BytesBE()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: Parameter{Type: Hash256Type, Value: util.Uint256{1, 2, 3}},
|
In: Parameter{Type: Hash256Type, Value: util.Uint256{1, 2, 3}},
|
||||||
Expected: util.Uint256{1, 2, 3},
|
Expected: util.Uint256{1, 2, 3},
|
||||||
|
ExpectedStackitem: stackitem.NewByteArray(util.Uint256{1, 2, 3}.BytesBE()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: Parameter{Type: PublicKeyType, Value: pk.PublicKey().Bytes()},
|
In: Parameter{Type: PublicKeyType, Value: pk.PublicKey().Bytes()},
|
||||||
Expected: pk.PublicKey().Bytes(),
|
Expected: pk.PublicKey().Bytes(),
|
||||||
|
ExpectedStackitem: stackitem.NewByteArray(pk.PublicKey().Bytes()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: Parameter{Type: SignatureType, Value: []byte{1, 2, 3}},
|
In: Parameter{Type: SignatureType, Value: []byte{1, 2, 3}},
|
||||||
Expected: []byte{1, 2, 3},
|
Expected: []byte{1, 2, 3},
|
||||||
|
ExpectedStackitem: stackitem.NewByteArray([]byte{1, 2, 3}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: Parameter{Type: AnyType},
|
In: Parameter{Type: AnyType},
|
||||||
Expected: nil,
|
Expected: nil,
|
||||||
|
ExpectedStackitem: stackitem.Null{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
In: Parameter{Type: ArrayType, Value: []Parameter{
|
In: Parameter{Type: ArrayType, Value: []Parameter{
|
||||||
|
@ -509,6 +521,13 @@ func TestExpandParameterToEmitable(t *testing.T) {
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
Expected: []interface{}{big.NewInt(123), []byte{1, 2, 3}, []interface{}{true}},
|
Expected: []interface{}{big.NewInt(123), []byte{1, 2, 3}, []interface{}{true}},
|
||||||
|
ExpectedStackitem: stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.NewBigInteger(big.NewInt(123)),
|
||||||
|
stackitem.NewByteArray([]byte{1, 2, 3}),
|
||||||
|
stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.NewBool(true),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
|
@ -519,6 +538,10 @@ func TestExpandParameterToEmitable(t *testing.T) {
|
||||||
|
|
||||||
emit.Array(bw.BinWriter, actual)
|
emit.Array(bw.BinWriter, actual)
|
||||||
require.NoError(t, bw.Err)
|
require.NoError(t, bw.Err)
|
||||||
|
|
||||||
|
actualSI, err := testCase.In.ToStackItem()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, testCase.ExpectedStackitem, actualSI)
|
||||||
}
|
}
|
||||||
errCases := []Parameter{
|
errCases := []Parameter{
|
||||||
{Type: UnknownType},
|
{Type: UnknownType},
|
||||||
|
|
|
@ -122,6 +122,18 @@ func Make(v interface{}) Item {
|
||||||
a = append(a, Make(i))
|
a = append(a, Make(i))
|
||||||
}
|
}
|
||||||
return Make(a)
|
return Make(a)
|
||||||
|
case []interface{}:
|
||||||
|
res := make([]Item, len(val))
|
||||||
|
for i := range val {
|
||||||
|
res[i] = Make(val[i])
|
||||||
|
}
|
||||||
|
return Make(res)
|
||||||
|
case util.Uint160:
|
||||||
|
return Make(val.BytesBE())
|
||||||
|
case util.Uint256:
|
||||||
|
return Make(val.BytesBE())
|
||||||
|
case nil:
|
||||||
|
return Null{}
|
||||||
default:
|
default:
|
||||||
i64T := reflect.TypeOf(int64(0))
|
i64T := reflect.TypeOf(int64(0))
|
||||||
if reflect.TypeOf(val).ConvertibleTo(i64T) {
|
if reflect.TypeOf(val).ConvertibleTo(i64T) {
|
||||||
|
|
|
@ -77,13 +77,17 @@ var makeStackItemTestCases = []struct {
|
||||||
input: []int{1, 2, 3},
|
input: []int{1, 2, 3},
|
||||||
result: &Array{value: []Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}},
|
result: &Array{value: []Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: nil,
|
||||||
|
result: Null{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var makeStackItemErrorCases = []struct {
|
var makeStackItemErrorCases = []struct {
|
||||||
input interface{}
|
input interface{}
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
input: nil,
|
input: map[int]int{1: 2},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue