mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-26 09:42:22 +00:00
Merge pull request #513 from nspcc-dev/feat/rpc_param
This PR does 3 things: adds array parameter unmarshalling extend Param with convenient methods refactor tests into using tables to make it easier add new tests (part of #347 solution)
This commit is contained in:
commit
ea9bc22510
7 changed files with 650 additions and 465 deletions
|
@ -18,7 +18,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
const (
|
||||
defaultDialTimeout = 4 * time.Second
|
||||
defaultRequestTimeout = 4 * time.Second
|
||||
defaultClientVersion = "2.0"
|
||||
|
@ -78,7 +78,6 @@ func NewClient(ctx context.Context, endpoint string, opts ClientOptions) (*Clien
|
|||
|
||||
// TODO(@antdm): Enable SSL.
|
||||
if opts.Cert != "" && opts.Key != "" {
|
||||
|
||||
}
|
||||
|
||||
if opts.Client.Timeout == 0 {
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -9,13 +14,76 @@ type (
|
|||
// the server or to send to a server using
|
||||
// the client.
|
||||
Param struct {
|
||||
StringVal string
|
||||
IntVal int
|
||||
Type string
|
||||
RawValue interface{}
|
||||
Type paramType
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
paramType int
|
||||
)
|
||||
|
||||
const (
|
||||
defaultT paramType = iota
|
||||
stringT
|
||||
numberT
|
||||
arrayT
|
||||
)
|
||||
|
||||
func (p Param) String() string {
|
||||
return fmt.Sprintf("%v", p.RawValue)
|
||||
return fmt.Sprintf("%v", p.Value)
|
||||
}
|
||||
|
||||
// GetString returns string value of the parameter.
|
||||
func (p Param) GetString() string { return p.Value.(string) }
|
||||
|
||||
// GetInt returns int value of te parameter.
|
||||
func (p Param) GetInt() int { return p.Value.(int) }
|
||||
|
||||
// GetUint256 returns Uint256 value of the parameter.
|
||||
func (p Param) GetUint256() (util.Uint256, error) {
|
||||
s, ok := p.Value.(string)
|
||||
if !ok {
|
||||
return util.Uint256{}, errors.New("must be a string")
|
||||
}
|
||||
|
||||
return util.Uint256DecodeReverseString(s)
|
||||
}
|
||||
|
||||
// GetBytesHex returns []byte value of the parameter if
|
||||
// it is a hex-encoded string.
|
||||
func (p Param) GetBytesHex() ([]byte, error) {
|
||||
s, ok := p.Value.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("must be a string")
|
||||
}
|
||||
|
||||
return hex.DecodeString(s)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||
func (p *Param) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err == nil {
|
||||
p.Type = stringT
|
||||
p.Value = s
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var num float64
|
||||
if err := json.Unmarshal(data, &num); err == nil {
|
||||
p.Type = numberT
|
||||
p.Value = int(num)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var ps []Param
|
||||
if err := json.Unmarshal(data, &ps); err == nil {
|
||||
p.Type = arrayT
|
||||
p.Value = ps
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("unknown type")
|
||||
}
|
||||
|
|
42
pkg/rpc/param_test.go
Normal file
42
pkg/rpc/param_test.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParam_UnmarshalJSON(t *testing.T) {
|
||||
msg := `["str1", 123, ["str2", 3]]`
|
||||
expected := Params{
|
||||
{
|
||||
Type: stringT,
|
||||
Value: "str1",
|
||||
},
|
||||
{
|
||||
Type: numberT,
|
||||
Value: 123,
|
||||
},
|
||||
{
|
||||
Type: arrayT,
|
||||
Value: []Param{
|
||||
{
|
||||
Type: stringT,
|
||||
Value: "str2",
|
||||
},
|
||||
{
|
||||
Type: numberT,
|
||||
Value: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var ps Params
|
||||
require.NoError(t, json.Unmarshal([]byte(msg), &ps))
|
||||
require.Equal(t, expected, ps)
|
||||
|
||||
msg = `[{"2": 3}]`
|
||||
require.Error(t, json.Unmarshal([]byte(msg), &ps))
|
||||
}
|
|
@ -1,49 +1,13 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type (
|
||||
// Params represents the JSON-RPC params.
|
||||
Params []Param
|
||||
)
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaller
|
||||
// interface.
|
||||
func (p *Params) UnmarshalJSON(data []byte) error {
|
||||
var params []interface{}
|
||||
|
||||
err := json.Unmarshal(data, ¶ms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < len(params); i++ {
|
||||
param := Param{
|
||||
RawValue: params[i],
|
||||
}
|
||||
|
||||
switch val := params[i].(type) {
|
||||
case string:
|
||||
param.StringVal = val
|
||||
param.Type = "string"
|
||||
|
||||
case float64:
|
||||
newVal, _ := params[i].(float64)
|
||||
param.IntVal = int(newVal)
|
||||
param.Type = "number"
|
||||
}
|
||||
|
||||
*p = append(*p, param)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValueAt returns the param struct for the given
|
||||
// Value returns the param struct for the given
|
||||
// index if it exists.
|
||||
func (p Params) ValueAt(index int) (*Param, bool) {
|
||||
func (p Params) Value(index int) (*Param, bool) {
|
||||
if len(p) > index {
|
||||
return &p[index], true
|
||||
}
|
||||
|
@ -51,33 +15,12 @@ func (p Params) ValueAt(index int) (*Param, bool) {
|
|||
return nil, false
|
||||
}
|
||||
|
||||
// ValueAtAndType returns the param struct at the given index if it
|
||||
// ValueWithType returns the param struct at the given index if it
|
||||
// exists and matches the given type.
|
||||
func (p Params) ValueAtAndType(index int, valueType string) (*Param, bool) {
|
||||
if len(p) > index && valueType == p[index].Type {
|
||||
return &p[index], true
|
||||
func (p Params) ValueWithType(index int, valType paramType) (*Param, bool) {
|
||||
if val, ok := p.Value(index); ok && val.Type == valType {
|
||||
return val, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Value returns the param struct for the given
|
||||
// index if it exists.
|
||||
func (p Params) Value(index int) (*Param, error) {
|
||||
if len(p) <= index {
|
||||
return nil, errInvalidParams
|
||||
}
|
||||
return &p[index], nil
|
||||
}
|
||||
|
||||
// ValueWithType returns the param struct at the given index if it
|
||||
// exists and matches the given type.
|
||||
func (p Params) ValueWithType(index int, valType string) (*Param, error) {
|
||||
val, err := p.Value(index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if val.Type != valType {
|
||||
return nil, errInvalidParams
|
||||
}
|
||||
return &p[index], nil
|
||||
}
|
||||
|
|
|
@ -30,11 +30,9 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
var (
|
||||
invalidBlockHeightError = func(index int, height int) error {
|
||||
var invalidBlockHeightError = func(index int, height int) error {
|
||||
return errors.Errorf("Param at index %d should be greater than or equal to 0 and less then or equal to current block height, got: %d", index, height)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// NewServer creates a new Server struct.
|
||||
func NewServer(chain core.Blockchainer, conf config.RPCConfig, coreServer *network.Server) Server {
|
||||
|
@ -123,27 +121,28 @@ Methods:
|
|||
getbestblockCalled.Inc()
|
||||
var hash util.Uint256
|
||||
|
||||
param, err := reqParams.Value(0)
|
||||
if err != nil {
|
||||
resultsErr = err
|
||||
param, ok := reqParams.Value(0)
|
||||
if !ok {
|
||||
resultsErr = errInvalidParams
|
||||
break Methods
|
||||
}
|
||||
|
||||
switch param.Type {
|
||||
case "string":
|
||||
hash, err = util.Uint256DecodeReverseString(param.StringVal)
|
||||
case stringT:
|
||||
var err error
|
||||
hash, err = param.GetUint256()
|
||||
if err != nil {
|
||||
resultsErr = errInvalidParams
|
||||
break Methods
|
||||
}
|
||||
case "number":
|
||||
case numberT:
|
||||
if !s.validBlockHeight(param) {
|
||||
resultsErr = errInvalidParams
|
||||
break Methods
|
||||
}
|
||||
|
||||
hash = s.chain.GetHeaderHash(param.IntVal)
|
||||
case "default":
|
||||
hash = s.chain.GetHeaderHash(param.GetInt())
|
||||
case defaultT:
|
||||
resultsErr = errInvalidParams
|
||||
break Methods
|
||||
}
|
||||
|
@ -161,16 +160,16 @@ Methods:
|
|||
|
||||
case "getblockhash":
|
||||
getblockHashCalled.Inc()
|
||||
param, err := reqParams.ValueWithType(0, "number")
|
||||
if err != nil {
|
||||
resultsErr = err
|
||||
param, ok := reqParams.ValueWithType(0, numberT)
|
||||
if !ok {
|
||||
resultsErr = errInvalidParams
|
||||
break Methods
|
||||
} else if !s.validBlockHeight(param) {
|
||||
resultsErr = invalidBlockHeightError(0, param.IntVal)
|
||||
resultsErr = invalidBlockHeightError(0, param.GetInt())
|
||||
break Methods
|
||||
}
|
||||
|
||||
results = s.chain.GetHeaderHash(param.IntVal)
|
||||
results = s.chain.GetHeaderHash(param.GetInt())
|
||||
|
||||
case "getconnectioncount":
|
||||
getconnectioncountCalled.Inc()
|
||||
|
@ -203,22 +202,22 @@ Methods:
|
|||
|
||||
case "validateaddress":
|
||||
validateaddressCalled.Inc()
|
||||
param, err := reqParams.Value(0)
|
||||
if err != nil {
|
||||
resultsErr = err
|
||||
param, ok := reqParams.Value(0)
|
||||
if !ok {
|
||||
resultsErr = errInvalidParams
|
||||
break Methods
|
||||
}
|
||||
results = wrappers.ValidateAddress(param.RawValue)
|
||||
results = wrappers.ValidateAddress(param.Value)
|
||||
|
||||
case "getassetstate":
|
||||
getassetstateCalled.Inc()
|
||||
param, err := reqParams.ValueWithType(0, "string")
|
||||
if err != nil {
|
||||
resultsErr = err
|
||||
param, ok := reqParams.ValueWithType(0, stringT)
|
||||
if !ok {
|
||||
resultsErr = errInvalidParams
|
||||
break Methods
|
||||
}
|
||||
|
||||
paramAssetID, err := util.Uint256DecodeReverseString(param.StringVal)
|
||||
paramAssetID, err := param.GetUint256()
|
||||
if err != nil {
|
||||
resultsErr = errInvalidParams
|
||||
break
|
||||
|
@ -266,14 +265,13 @@ func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
|
|||
var resultsErr error
|
||||
var results interface{}
|
||||
|
||||
param0, err := reqParams.ValueWithType(0, "string")
|
||||
if err != nil {
|
||||
resultsErr = err
|
||||
} else if txHash, err := util.Uint256DecodeReverseString(param0.StringVal); err != nil {
|
||||
if param0, ok := reqParams.Value(0); !ok {
|
||||
return nil, errInvalidParams
|
||||
} else if txHash, err := param0.GetUint256(); err != nil {
|
||||
resultsErr = errInvalidParams
|
||||
} else if tx, height, err := s.chain.GetTransaction(txHash); err != nil {
|
||||
err = errors.Wrapf(err, "Invalid transaction hash: %s", txHash)
|
||||
resultsErr = NewInvalidParamsError(err.Error(), err)
|
||||
return nil, NewInvalidParamsError(err.Error(), err)
|
||||
} else if len(reqParams) >= 2 {
|
||||
_header := s.chain.GetHeaderHash(int(height))
|
||||
header, err := s.chain.GetHeader(_header)
|
||||
|
@ -281,8 +279,8 @@ func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
|
|||
resultsErr = NewInvalidParamsError(err.Error(), err)
|
||||
}
|
||||
|
||||
param1, _ := reqParams.ValueAt(1)
|
||||
switch v := param1.RawValue.(type) {
|
||||
param1, _ := reqParams.Value(1)
|
||||
switch v := param1.Value.(type) {
|
||||
|
||||
case int, float64, bool, string:
|
||||
if v == 0 || v == "0" || v == 0.0 || v == false || v == "false" {
|
||||
|
@ -305,14 +303,14 @@ func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{},
|
|||
var resultsErr error
|
||||
var results interface{}
|
||||
|
||||
param, err := reqParams.ValueWithType(0, "string")
|
||||
if err != nil {
|
||||
resultsErr = err
|
||||
} else if scriptHash, err := crypto.Uint160DecodeAddress(param.StringVal); err != nil {
|
||||
resultsErr = errInvalidParams
|
||||
param, ok := reqParams.ValueWithType(0, stringT)
|
||||
if !ok {
|
||||
return nil, errInvalidParams
|
||||
} else if scriptHash, err := crypto.Uint160DecodeAddress(param.GetString()); err != nil {
|
||||
return nil, errInvalidParams
|
||||
} else if as := s.chain.GetAccountState(scriptHash); as != nil {
|
||||
if unspents {
|
||||
results = wrappers.NewUnspents(as, s.chain, param.StringVal)
|
||||
results = wrappers.NewUnspents(as, s.chain, param.GetString())
|
||||
} else {
|
||||
results = wrappers.NewAccountState(as)
|
||||
}
|
||||
|
@ -324,21 +322,22 @@ func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{},
|
|||
|
||||
// invokescript implements the `invokescript` RPC call.
|
||||
func (s *Server) invokescript(reqParams Params) (interface{}, error) {
|
||||
hexScript, err := reqParams.ValueWithType(0, "string")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if len(reqParams) < 1 {
|
||||
return nil, errInvalidParams
|
||||
}
|
||||
script, err := hex.DecodeString(hexScript.StringVal)
|
||||
|
||||
script, err := reqParams[0].GetBytesHex()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errInvalidParams
|
||||
}
|
||||
|
||||
vm, _ := s.chain.GetTestVM()
|
||||
vm.LoadScript(script)
|
||||
_ = vm.Run()
|
||||
result := &wrappers.InvokeResult{
|
||||
State: vm.State(),
|
||||
GasConsumed: "0.1",
|
||||
Script: hexScript.StringVal,
|
||||
Script: reqParams[0].GetString(),
|
||||
Stack: vm.Estack(),
|
||||
}
|
||||
return result, nil
|
||||
|
@ -348,11 +347,10 @@ func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) {
|
|||
var resultsErr error
|
||||
var results interface{}
|
||||
|
||||
param, err := reqParams.ValueWithType(0, "string")
|
||||
if err != nil {
|
||||
resultsErr = err
|
||||
} else if byteTx, err := hex.DecodeString(param.StringVal); err != nil {
|
||||
resultsErr = errInvalidParams
|
||||
if len(reqParams) < 1 {
|
||||
return nil, errInvalidParams
|
||||
} else if byteTx, err := reqParams[0].GetBytesHex(); err != nil {
|
||||
return nil, errInvalidParams
|
||||
} else {
|
||||
r := io.NewBinReaderFromBuf(byteTx)
|
||||
tx := &transaction.Transaction{}
|
||||
|
@ -387,5 +385,5 @@ func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) {
|
|||
}
|
||||
|
||||
func (s Server) validBlockHeight(param *Param) bool {
|
||||
return param.IntVal >= 0 && param.IntVal <= int(s.chain.BlockHeight())
|
||||
return param.GetInt() >= 0 && param.GetInt() <= int(s.chain.BlockHeight())
|
||||
}
|
||||
|
|
|
@ -7,175 +7,335 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/wrappers"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type executor struct {
|
||||
chain *core.Blockchain
|
||||
handler http.HandlerFunc
|
||||
}
|
||||
|
||||
const (
|
||||
defaultJSONRPC = "2.0"
|
||||
defaultID = 1
|
||||
)
|
||||
|
||||
type rpcTestCase struct {
|
||||
name string
|
||||
params string
|
||||
fail bool
|
||||
result func(e *executor) interface{}
|
||||
check func(t *testing.T, e *executor, result interface{})
|
||||
}
|
||||
|
||||
var rpcTestCases = map[string][]rpcTestCase{
|
||||
"getaccountstate": {
|
||||
{
|
||||
name: "positive",
|
||||
params: `["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]`,
|
||||
result: func(e *executor) interface{} { return &GetAccountStateResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetAccountStateResponse)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 1, len(res.Result.Balances))
|
||||
assert.Equal(t, false, res.Result.Frozen)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "negative",
|
||||
params: `["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]`,
|
||||
result: func(e *executor) interface{} { return "Invalid public account address" },
|
||||
},
|
||||
{
|
||||
name: "no params",
|
||||
params: `[]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid address",
|
||||
params: `["notabase58"]`,
|
||||
fail: true,
|
||||
},
|
||||
},
|
||||
"getassetstate": {
|
||||
{
|
||||
name: "positive",
|
||||
params: `["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"]`,
|
||||
result: func(e *executor) interface{} { return &GetAssetResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetAssetResponse)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "00", res.Result.Owner)
|
||||
assert.Equal(t, "AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3", res.Result.Admin)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "negative",
|
||||
params: `["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de2"]`,
|
||||
result: func(e *executor) interface{} { return "Invalid assetid" },
|
||||
},
|
||||
{
|
||||
name: "no params",
|
||||
params: `[]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid hash",
|
||||
params: `["notahex"]`,
|
||||
fail: true,
|
||||
},
|
||||
},
|
||||
"getbestblockhash": {
|
||||
{
|
||||
params: "[]",
|
||||
result: func(e *executor) interface{} {
|
||||
return "0x" + e.chain.CurrentBlockHash().ReverseString()
|
||||
},
|
||||
},
|
||||
{
|
||||
params: "1",
|
||||
fail: true,
|
||||
},
|
||||
},
|
||||
"getblock": {
|
||||
{
|
||||
name: "positive",
|
||||
params: "[1]",
|
||||
result: func(e *executor) interface{} { return &GetBlockResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetBlockResponse)
|
||||
require.True(t, ok)
|
||||
|
||||
block, err := e.chain.GetBlock(e.chain.GetHeaderHash(1))
|
||||
require.NoErrorf(t, err, "could not get block")
|
||||
|
||||
expectedHash := "0x" + block.Hash().ReverseString()
|
||||
assert.Equal(t, expectedHash, res.Result.Hash)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no params",
|
||||
params: `[]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid height",
|
||||
params: `[-1]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid hash",
|
||||
params: `["notahex"]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "missing hash",
|
||||
params: `["` + util.Uint256{}.String() + `"]`,
|
||||
fail: true,
|
||||
},
|
||||
},
|
||||
"getblockcount": {
|
||||
{
|
||||
params: "[]",
|
||||
result: func(e *executor) interface{} { return int(e.chain.BlockHeight() + 1) },
|
||||
},
|
||||
},
|
||||
"getblockhash": {
|
||||
{
|
||||
params: "[1]",
|
||||
result: func(e *executor) interface{} { return "" },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*StringResultResponse)
|
||||
require.True(t, ok)
|
||||
|
||||
block, err := e.chain.GetBlock(e.chain.GetHeaderHash(1))
|
||||
require.NoErrorf(t, err, "could not get block")
|
||||
|
||||
expectedHash := "0x" + block.Hash().ReverseString()
|
||||
assert.Equal(t, expectedHash, res.Result)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "string height",
|
||||
params: `["first"]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid number height",
|
||||
params: `[-2]`,
|
||||
fail: true,
|
||||
},
|
||||
},
|
||||
"getconnectioncount": {
|
||||
{
|
||||
params: "[]",
|
||||
result: func(*executor) interface{} { return 0 },
|
||||
},
|
||||
},
|
||||
"getpeers": {
|
||||
{
|
||||
params: "[]",
|
||||
result: func(*executor) interface{} {
|
||||
return &GetPeersResponse{
|
||||
Jsonrpc: defaultJSONRPC,
|
||||
Result: struct {
|
||||
Unconnected []int `json:"unconnected"`
|
||||
Connected []int `json:"connected"`
|
||||
Bad []int `json:"bad"`
|
||||
}{
|
||||
Unconnected: []int{},
|
||||
Connected: []int{},
|
||||
Bad: []int{},
|
||||
},
|
||||
ID: defaultID,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"getrawtransaction": {
|
||||
{
|
||||
name: "no params",
|
||||
params: `[]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid hash",
|
||||
params: `["notahex"]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "missing hash",
|
||||
params: `["` + util.Uint256{}.String() + `"]`,
|
||||
fail: true,
|
||||
},
|
||||
},
|
||||
"getunspents": {
|
||||
{
|
||||
name: "positive",
|
||||
params: `["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]`,
|
||||
result: func(e *executor) interface{} { return &GetUnspents{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetUnspents)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, 1, len(res.Result.Balance))
|
||||
assert.Equal(t, 1, len(res.Result.Balance[0].Unspents))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "negative",
|
||||
params: `["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]`,
|
||||
result: func(e *executor) interface{} { return "Invalid public account address" },
|
||||
},
|
||||
},
|
||||
"getversion": {
|
||||
{
|
||||
params: "[]",
|
||||
result: func(*executor) interface{} { return &GetVersionResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
resp, ok := result.(*GetVersionResponse)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "/NEO-GO:/", resp.Result.UserAgent)
|
||||
},
|
||||
},
|
||||
},
|
||||
"sendrawtransaction": {
|
||||
{
|
||||
name: "positive",
|
||||
params: `["d1001b00046e616d6567d3d8602814a429a91afdbaa3914884a1c90c733101201cc9c05cefffe6cdd7b182816a9152ec218d2ec000000141403387ef7940a5764259621e655b3c621a6aafd869a611ad64adcc364d8dd1edf84e00a7f8b11b630a377eaef02791d1c289d711c08b7ad04ff0d6c9caca22cfe6232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"]`,
|
||||
result: func(e *executor) interface{} { return &SendTXResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*SendTXResponse)
|
||||
require.True(t, ok)
|
||||
assert.True(t, res.Result)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "negative",
|
||||
params: `["0274d792072617720636f6e7472616374207472616e73616374696f6e206465736372697074696f6e01949354ea0a8b57dfee1e257a1aedd1e0eea2e5837de145e8da9c0f101bfccc8e0100029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500a3e11100000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5004f2418010000001cc9c05cefffe6cdd7b182816a9152ec218d2ec0014140dbd3cddac5cb2bd9bf6d93701f1a6f1c9dbe2d1b480c54628bbb2a4d536158c747a6af82698edf9f8af1cac3850bcb772bd9c8e4ac38f80704751cc4e0bd0e67232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "no params",
|
||||
params: `[]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid string",
|
||||
params: `["notahex"]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid tx",
|
||||
params: `["0274d792072617720636f6e747261637"]`,
|
||||
fail: true,
|
||||
},
|
||||
},
|
||||
"validateaddress": {
|
||||
{
|
||||
name: "positive",
|
||||
params: `["AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"]`,
|
||||
result: func(*executor) interface{} { return &ValidateAddrResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*ValidateAddrResponse)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i", res.Result.Address)
|
||||
assert.True(t, res.Result.IsValid)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "negative",
|
||||
params: "[1]",
|
||||
result: func(*executor) interface{} {
|
||||
return &ValidateAddrResponse{
|
||||
Jsonrpc: defaultJSONRPC,
|
||||
Result: wrappers.ValidateAddressResponse{
|
||||
Address: float64(1),
|
||||
IsValid: false,
|
||||
},
|
||||
ID: defaultID,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestRPC(t *testing.T) {
|
||||
chain, handler := initServerWithInMemoryChain(t)
|
||||
|
||||
t.Run("getbestblockhash", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getbestblockhash", "params": []}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res StringResultResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, "0x"+chain.CurrentBlockHash().ReverseString(), res.Result)
|
||||
})
|
||||
e := &executor{chain: chain, handler: handler}
|
||||
for method, cases := range rpcTestCases {
|
||||
t.Run(method, func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "%s", "params": %s}`
|
||||
|
||||
t.Run("getblock", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblock", "params": [1]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res GetBlockResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
block, err := chain.GetBlock(chain.GetHeaderHash(1))
|
||||
assert.NoErrorf(t, err, "could not get block")
|
||||
expectedHash := "0x" + block.Hash().ReverseString()
|
||||
assert.Equal(t, expectedHash, res.Result.Hash)
|
||||
})
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
body := doRPCCall(fmt.Sprintf(rpc, method, tc.params), handler, t)
|
||||
checkErrResponse(t, body, tc.fail)
|
||||
if tc.fail {
|
||||
return
|
||||
}
|
||||
|
||||
t.Run("getblockcount", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblockcount", "params": []}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res IntResultResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, chain.BlockHeight()+1, uint32(res.Result))
|
||||
})
|
||||
expected, res := tc.getResultPair(e)
|
||||
err := json.Unmarshal(body, res)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
|
||||
t.Run("getblockhash", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [1]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res StringResultResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
block, err := chain.GetBlock(chain.GetHeaderHash(1))
|
||||
assert.NoErrorf(t, err, "could not get block")
|
||||
expectedHash := "0x" + block.Hash().ReverseString()
|
||||
assert.Equal(t, expectedHash, res.Result)
|
||||
if tc.check == nil {
|
||||
assert.Equal(t, expected, res)
|
||||
} else {
|
||||
tc.check(t, e, res)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("getconnectioncount", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getconnectioncount", "params": []}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res IntResultResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, 0, res.Result)
|
||||
})
|
||||
|
||||
t.Run("getversion", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getversion", "params": []}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res GetVersionResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, "/NEO-GO:/", res.Result.UserAgent)
|
||||
})
|
||||
|
||||
t.Run("getpeers", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getpeers", "params": []}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res GetPeersResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, []int{}, res.Result.Bad)
|
||||
assert.Equal(t, []int{}, res.Result.Unconnected)
|
||||
assert.Equal(t, []int{}, res.Result.Connected)
|
||||
})
|
||||
|
||||
t.Run("validateaddress_positive", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": ["AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res ValidateAddrResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, true, res.Result.IsValid)
|
||||
})
|
||||
|
||||
t.Run("validateaddress_negative", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": [1]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res ValidateAddrResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, false, res.Result.IsValid)
|
||||
})
|
||||
|
||||
t.Run("getassetstate_positive", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res GetAssetResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, "00", res.Result.Owner)
|
||||
assert.Equal(t, "AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3", res.Result.Admin)
|
||||
})
|
||||
|
||||
t.Run("getassetstate_negative", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de2"]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res StringResultResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, "Invalid assetid", res.Result)
|
||||
})
|
||||
|
||||
t.Run("getaccountstate_positive", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": ["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res GetAccountStateResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, 1, len(res.Result.Balances))
|
||||
assert.Equal(t, false, res.Result.Frozen)
|
||||
})
|
||||
|
||||
t.Run("getunspents_positive", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getunspents", "params": ["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res GetUnspents
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, 1, len(res.Result.Balance))
|
||||
assert.Equal(t, 1, len(res.Result.Balance[0].Unspents))
|
||||
})
|
||||
|
||||
t.Run("getaccountstate_negative", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": ["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res StringResultResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, "Invalid public account address", res.Result)
|
||||
})
|
||||
|
||||
t.Run("getunspents_negative", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getunspents", "params": ["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res StringResultResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, "Invalid public account address", res.Result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("getrawtransaction", func(t *testing.T) {
|
||||
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
||||
|
@ -184,32 +344,53 @@ func TestRPC(t *testing.T) {
|
|||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res StringResultResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
err := json.Unmarshal(body, &res)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res.Result)
|
||||
})
|
||||
|
||||
t.Run("sendrawtransaction_positive", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "sendrawtransaction", "params": ["d1001b00046e616d6567d3d8602814a429a91afdbaa3914884a1c90c733101201cc9c05cefffe6cdd7b182816a9152ec218d2ec000000141403387ef7940a5764259621e655b3c621a6aafd869a611ad64adcc364d8dd1edf84e00a7f8b11b630a377eaef02791d1c289d711c08b7ad04ff0d6c9caca22cfe6232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"]}`
|
||||
t.Run("getrawtransaction 2 arguments", func(t *testing.T) {
|
||||
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
||||
TXHash := block.Transactions[1].Hash()
|
||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, TXHash.ReverseString())
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res SendTXResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &res)
|
||||
assert.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, true, res.Result)
|
||||
var res StringResultResponse
|
||||
err := json.Unmarshal(body, &res)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res.Result)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("sendrawtransaction_negative", func(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "sendrawtransaction", "params": ["0274d792072617720636f6e7472616374207472616e73616374696f6e206465736372697074696f6e01949354ea0a8b57dfee1e257a1aedd1e0eea2e5837de145e8da9c0f101bfccc8e0100029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500a3e11100000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5004f2418010000001cc9c05cefffe6cdd7b182816a9152ec218d2ec0014140dbd3cddac5cb2bd9bf6d93701f1a6f1c9dbe2d1b480c54628bbb2a4d536158c747a6af82698edf9f8af1cac3850bcb772bd9c8e4ac38f80704751cc4e0bd0e67232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"]}`
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, true)
|
||||
})
|
||||
func (tc rpcTestCase) getResultPair(e *executor) (expected interface{}, res interface{}) {
|
||||
expected = tc.result(e)
|
||||
switch exp := expected.(type) {
|
||||
case string:
|
||||
res = new(StringResultResponse)
|
||||
expected = &StringResultResponse{
|
||||
Jsonrpc: defaultJSONRPC,
|
||||
Result: exp,
|
||||
ID: defaultID,
|
||||
}
|
||||
case int:
|
||||
res = new(IntResultResponse)
|
||||
expected = &IntResultResponse{
|
||||
Jsonrpc: defaultJSONRPC,
|
||||
Result: exp,
|
||||
ID: defaultID,
|
||||
}
|
||||
default:
|
||||
resVal := reflect.New(reflect.TypeOf(expected).Elem())
|
||||
res = resVal.Interface()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkErrResponse(t *testing.T, body []byte, expectingFail bool) {
|
||||
var errresp ErrorResponse
|
||||
err := json.Unmarshal(bytes.TrimSpace(body), &errresp)
|
||||
assert.Nil(t, err)
|
||||
err := json.Unmarshal(body, &errresp)
|
||||
require.Nil(t, err)
|
||||
if expectingFail {
|
||||
assert.NotEqual(t, 0, errresp.Error.Code)
|
||||
assert.NotEqual(t, "", errresp.Error.Message)
|
||||
|
@ -227,5 +408,5 @@ func doRPCCall(rpcCall string, handler http.HandlerFunc, t *testing.T) []byte {
|
|||
resp := w.Result()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
assert.NoErrorf(t, err, "could not read response from the request: %s", rpcCall)
|
||||
return body
|
||||
return bytes.TrimSpace(body)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var testCases = []struct {
|
||||
|
@ -41,6 +41,28 @@ var testCases = []struct {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `{"type": "Hash160", "value": "0bcd2978634d961c24f5aea0802297ff128724d6"}`,
|
||||
result: StackParam{
|
||||
Type: Hash160,
|
||||
Value: util.Uint160{
|
||||
0x0b, 0xcd, 0x29, 0x78, 0x63, 0x4d, 0x96, 0x1c, 0x24, 0xf5,
|
||||
0xae, 0xa0, 0x80, 0x22, 0x97, 0xff, 0x12, 0x87, 0x24, 0xd6,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `{"type": "Hash256", "value": "f037308fa0ab18155bccfc08485468c112409ea5064595699e98c545f245f32d"}`,
|
||||
result: StackParam{
|
||||
Type: Hash256,
|
||||
Value: util.Uint256{
|
||||
0x2d, 0xf3, 0x45, 0xf2, 0x45, 0xc5, 0x98, 0x9e,
|
||||
0x69, 0x95, 0x45, 0x06, 0xa5, 0x9e, 0x40, 0x12,
|
||||
0xc1, 0x68, 0x54, 0x48, 0x08, 0xfc, 0xcc, 0x5b,
|
||||
0x15, 0x18, 0xab, 0xa0, 0x8f, 0x30, 0x37, 0xf0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var errorCases = []string{
|
||||
|
@ -55,192 +77,124 @@ var errorCases = []string{
|
|||
`{"type": "Hash160","value": "0bcd"}`, // incorrect Uint160 value
|
||||
`{"type": "Hash256","value": "0bcd"}`, // incorrect Uint256 value
|
||||
`{"type": "Stringg","value": ""}`, // incorrect type
|
||||
`{"type": {},"value": ""}`, // incorrect value
|
||||
|
||||
`{"type": "InteropInterface","value": ""}`, // ununmarshable type
|
||||
}
|
||||
|
||||
func TestStackParam_UnmarshalJSON(t *testing.T) {
|
||||
var (
|
||||
err error
|
||||
r, s StackParam
|
||||
)
|
||||
var s StackParam
|
||||
for _, tc := range testCases {
|
||||
if err = json.Unmarshal([]byte(tc.input), &s); err != nil {
|
||||
t.Errorf("error while unmarhsalling: %v", err)
|
||||
} else if !reflect.DeepEqual(s, tc.result) {
|
||||
t.Errorf("got (%v), expected (%v)", s, tc.result)
|
||||
}
|
||||
}
|
||||
|
||||
// Hash160 unmarshalling
|
||||
err = json.Unmarshal([]byte(`{"type": "Hash160","value": "0bcd2978634d961c24f5aea0802297ff128724d6"}`), &s)
|
||||
if err != nil {
|
||||
t.Errorf("error while unmarhsalling: %v", err)
|
||||
}
|
||||
|
||||
h160, err := util.Uint160DecodeString("0bcd2978634d961c24f5aea0802297ff128724d6")
|
||||
if err != nil {
|
||||
t.Errorf("unmarshal error: %v", err)
|
||||
}
|
||||
|
||||
if r = (StackParam{Type: Hash160, Value: h160}); !reflect.DeepEqual(s, r) {
|
||||
t.Errorf("got (%v), expected (%v)", s, r)
|
||||
}
|
||||
|
||||
// Hash256 unmarshalling
|
||||
err = json.Unmarshal([]byte(`{"type": "Hash256","value": "f037308fa0ab18155bccfc08485468c112409ea5064595699e98c545f245f32d"}`), &s)
|
||||
if err != nil {
|
||||
t.Errorf("error while unmarhsalling: %v", err)
|
||||
}
|
||||
h256, err := util.Uint256DecodeReverseString("f037308fa0ab18155bccfc08485468c112409ea5064595699e98c545f245f32d")
|
||||
if err != nil {
|
||||
t.Errorf("unmarshal error: %v", err)
|
||||
}
|
||||
if r = (StackParam{Type: Hash256, Value: h256}); !reflect.DeepEqual(s, r) {
|
||||
t.Errorf("got (%v), expected (%v)", s, r)
|
||||
assert.NoError(t, json.Unmarshal([]byte(tc.input), &s))
|
||||
assert.Equal(t, s, tc.result)
|
||||
}
|
||||
|
||||
for _, input := range errorCases {
|
||||
if err = json.Unmarshal([]byte(input), &s); err == nil {
|
||||
t.Errorf("expected error, got (nil)")
|
||||
}
|
||||
assert.Error(t, json.Unmarshal([]byte(input), &s))
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
hash160 = "0bcd2978634d961c24f5aea0802297ff128724d6"
|
||||
hash256 = "7fe610b7c8259ae949accacb091a1bc53219c51a1cb8752fbc6457674c13ec0b"
|
||||
testString = "myteststring"
|
||||
)
|
||||
var tryParseTestCases = []struct {
|
||||
input interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
input: []byte{
|
||||
0x0b, 0xcd, 0x29, 0x78, 0x63, 0x4d, 0x96, 0x1c, 0x24, 0xf5,
|
||||
0xae, 0xa0, 0x80, 0x22, 0x97, 0xff, 0x12, 0x87, 0x24, 0xd6,
|
||||
},
|
||||
expected: util.Uint160{
|
||||
0x0b, 0xcd, 0x29, 0x78, 0x63, 0x4d, 0x96, 0x1c, 0x24, 0xf5,
|
||||
0xae, 0xa0, 0x80, 0x22, 0x97, 0xff, 0x12, 0x87, 0x24, 0xd6,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: []byte{
|
||||
0xf0, 0x37, 0x30, 0x8f, 0xa0, 0xab, 0x18, 0x15,
|
||||
0x5b, 0xcc, 0xfc, 0x08, 0x48, 0x54, 0x68, 0xc1,
|
||||
0x12, 0x40, 0x9e, 0xa5, 0x06, 0x45, 0x95, 0x69,
|
||||
0x9e, 0x98, 0xc5, 0x45, 0xf2, 0x45, 0xf3, 0x2d,
|
||||
},
|
||||
expected: util.Uint256{
|
||||
0x2d, 0xf3, 0x45, 0xf2, 0x45, 0xc5, 0x98, 0x9e,
|
||||
0x69, 0x95, 0x45, 0x06, 0xa5, 0x9e, 0x40, 0x12,
|
||||
0xc1, 0x68, 0x54, 0x48, 0x08, 0xfc, 0xcc, 0x5b,
|
||||
0x15, 0x18, 0xab, 0xa0, 0x8f, 0x30, 0x37, 0xf0,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: []byte{0, 1, 2, 3, 4, 9, 8, 6},
|
||||
expected: []byte{0, 1, 2, 3, 4, 9, 8, 6},
|
||||
},
|
||||
{
|
||||
input: []byte{0x63, 0x78, 0x29, 0xcd, 0x0b},
|
||||
expected: int64(50686687331),
|
||||
},
|
||||
{
|
||||
input: []byte("this is a test string"),
|
||||
expected: "this is a test string",
|
||||
},
|
||||
}
|
||||
|
||||
func TestStackParam_TryParse(t *testing.T) {
|
||||
// ByteArray to util.Uint160 conversion
|
||||
data, err := hex.DecodeString(hash160)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var (
|
||||
outputUint160, expectedUint160 util.Uint160
|
||||
input = StackParam{
|
||||
for _, tc := range tryParseTestCases {
|
||||
t.Run(reflect.TypeOf(tc.expected).String(), func(t *testing.T) {
|
||||
input := StackParam{
|
||||
Type: ByteArray,
|
||||
Value: data,
|
||||
}
|
||||
)
|
||||
expectedUint160, err = util.Uint160DecodeString(hash160)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = input.TryParse(&outputUint160); err != nil {
|
||||
t.Errorf("failed to parse stackparam to Uint160: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(outputUint160, expectedUint160) {
|
||||
t.Errorf("got (%v), expected (%v)", outputUint160, expectedUint160)
|
||||
Value: tc.input,
|
||||
}
|
||||
|
||||
// ByteArray to util.Uint256 conversion
|
||||
data, err = hex.DecodeString(hash256)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
val := reflect.New(reflect.TypeOf(tc.expected))
|
||||
assert.NoError(t, input.TryParse(val.Interface()))
|
||||
assert.Equal(t, tc.expected, val.Elem().Interface())
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
outputUint256, expectedUint256 util.Uint256
|
||||
uint256input = StackParam{
|
||||
t.Run("[]Uint160", func(t *testing.T) {
|
||||
exp1 := util.Uint160{1, 2, 3, 4, 5}
|
||||
exp2 := util.Uint160{9, 8, 7, 6, 5}
|
||||
|
||||
params := StackParams{
|
||||
{
|
||||
Type: ByteArray,
|
||||
Value: data,
|
||||
}
|
||||
)
|
||||
expectedUint256, err = util.Uint256DecodeReverseString(hash256)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = uint256input.TryParse(&outputUint256); err != nil {
|
||||
t.Errorf("failed to parse stackparam to []byte: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(outputUint256, expectedUint256) {
|
||||
t.Errorf("got (%v), expected (%v)", outputUint256, expectedUint256)
|
||||
}
|
||||
|
||||
// ByteArray to []byte conversion
|
||||
var (
|
||||
outputBytes []byte
|
||||
expectedBytes = expectedUint160.Bytes()
|
||||
)
|
||||
if err = input.TryParse(&outputBytes); err != nil {
|
||||
t.Errorf("failed to parse stackparam to []byte: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(outputBytes, expectedBytes) {
|
||||
t.Errorf("got (%v), expected (%v)", outputBytes, expectedBytes)
|
||||
}
|
||||
|
||||
// ByteArray to int64 conversion
|
||||
data, err = hex.DecodeString("637829cd0b")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var (
|
||||
outputInt, expectedInt int64
|
||||
intinput = StackParam{
|
||||
Type: ByteArray,
|
||||
Value: data,
|
||||
}
|
||||
)
|
||||
expectedInt = 50686687331
|
||||
if err = intinput.TryParse(&outputInt); err != nil {
|
||||
t.Errorf("failed to parse stackparam to []byte: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(outputInt, expectedInt) {
|
||||
t.Errorf("got (%v), expected (%v)", outputInt, expectedInt)
|
||||
}
|
||||
|
||||
// ByteArray to string conversion
|
||||
data = []byte(testString)
|
||||
var (
|
||||
outputStr, expectedStr string
|
||||
strinput = StackParam{
|
||||
Type: ByteArray,
|
||||
Value: data,
|
||||
}
|
||||
)
|
||||
expectedStr = testString
|
||||
if err = strinput.TryParse(&outputStr); err != nil {
|
||||
t.Errorf("failed to parse stackparam to []byte: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(outputStr, expectedStr) {
|
||||
t.Errorf("got (%v), expected (%v)", outputStr, expectedStr)
|
||||
}
|
||||
|
||||
// StackParams to []util.Uint160
|
||||
data, err = hex.DecodeString(hash160)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expUint160, err := util.Uint160DecodeString(hash160)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var (
|
||||
params = StackParams{
|
||||
StackParam{
|
||||
Type: ByteArray,
|
||||
Value: data,
|
||||
Value: exp1.Bytes(),
|
||||
},
|
||||
StackParam{
|
||||
{
|
||||
Type: ByteArray,
|
||||
Value: data,
|
||||
Value: exp2.Bytes(),
|
||||
},
|
||||
}
|
||||
expectedArray = []util.Uint160{
|
||||
expUint160,
|
||||
expUint160,
|
||||
}
|
||||
out1, out2 = &util.Uint160{}, &util.Uint160{}
|
||||
)
|
||||
if err = params.TryParseArray(out1, out2); err != nil {
|
||||
t.Errorf("failed to parse stackparam to []byte: %v", err)
|
||||
}
|
||||
outArray := []util.Uint160{*out1, *out2}
|
||||
if !reflect.DeepEqual(outArray, expectedArray) {
|
||||
t.Errorf("got (%v), expected (%v)", outArray, expectedArray)
|
||||
}
|
||||
|
||||
var out1, out2 util.Uint160
|
||||
|
||||
assert.NoError(t, params.TryParseArray(&out1, &out2))
|
||||
assert.Equal(t, exp1, out1)
|
||||
assert.Equal(t, exp2, out2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStackParamType_String(t *testing.T) {
|
||||
types := []StackParamType{
|
||||
Signature,
|
||||
Boolean,
|
||||
Integer,
|
||||
Hash160,
|
||||
Hash256,
|
||||
ByteArray,
|
||||
PublicKey,
|
||||
String,
|
||||
Array,
|
||||
InteropInterface,
|
||||
Void,
|
||||
}
|
||||
|
||||
for _, exp := range types {
|
||||
actual, err := StackParamTypeFromString(exp.String())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, exp, actual)
|
||||
}
|
||||
|
||||
actual, err := StackParamTypeFromString(Unknown.String())
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, Unknown, actual)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue