rpc: separate out request and response structures

Mostly as is, no real effort done yet to optimize them, so there are still a
lot of duplicates there, but at least we sort them out into different smaller
packages.
This commit is contained in:
Roman Khimov 2020-01-14 15:02:38 +03:00
parent 69e1ad512f
commit f330f2f40b
18 changed files with 422 additions and 396 deletions

View file

@ -15,6 +15,8 @@ import (
"github.com/CityOfZion/neo-go/pkg/crypto/hash" "github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/rpc" "github.com/CityOfZion/neo-go/pkg/rpc"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/rpc/response"
"github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/smartcontract"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm" "github.com/CityOfZion/neo-go/pkg/vm"
@ -294,10 +296,10 @@ func initSmartContract(ctx *cli.Context) error {
// TODO: Fix the missing neo-go.yml file with the `init` command when the package manager is in place. // TODO: Fix the missing neo-go.yml file with the `init` command when the package manager is in place.
if !ctx.Bool("skip-details") { if !ctx.Bool("skip-details") {
details := parseContractDetails() details := parseContractDetails()
details.ReturnType = rpc.ByteArray details.ReturnType = request.ByteArray
details.Parameters = make([]rpc.StackParamType, 2) details.Parameters = make([]request.StackParamType, 2)
details.Parameters[0] = rpc.String details.Parameters[0] = request.String
details.Parameters[1] = rpc.Array details.Parameters[1] = request.Array
project := &ProjectConfig{Contract: details} project := &ProjectConfig{Contract: details}
b, err := yaml.Marshal(project) b, err := yaml.Marshal(project)
@ -362,7 +364,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
operation string operation string
params = make([]smartcontract.Parameter, 0) params = make([]smartcontract.Parameter, 0)
paramsStart = 1 paramsStart = 1
resp *rpc.InvokeScriptResponse resp *response.InvokeScript
wif *keys.WIF wif *keys.WIF
) )

View file

@ -14,6 +14,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/state"
"github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -177,13 +178,13 @@ func (c *Client) CalculateInputs(address string, asset util.Uint256, cost util.F
} }
func (c *Client) performRequest(method string, p params, v interface{}) error { func (c *Client) performRequest(method string, p request.RawParams, v interface{}) error {
var ( var (
r = request{ r = request.Raw{
JSONRPC: c.version, JSONRPC: c.version,
Method: method, Method: method,
Params: p.values, RawParams: p.Values,
ID: 1, ID: 1,
} }
buf = new(bytes.Buffer) buf = new(bytes.Buffer)
) )

View file

@ -1,128 +0,0 @@
package rpc
import (
"encoding/json"
"io"
"net/http"
"github.com/pkg/errors"
"go.uber.org/zap"
)
const (
jsonRPCVersion = "2.0"
)
type (
// Request represents a standard JSON-RPC 2.0
// request: http://www.jsonrpc.org/specification#request_object.
Request struct {
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
RawParams json.RawMessage `json:"params,omitempty"`
RawID json.RawMessage `json:"id,omitempty"`
}
// Response represents a standard JSON-RPC 2.0
// response: http://www.jsonrpc.org/specification#response_object.
Response struct {
JSONRPC string `json:"jsonrpc"`
Result interface{} `json:"result,omitempty"`
Error *Error `json:"error,omitempty"`
ID json.RawMessage `json:"id,omitempty"`
}
)
// NewRequest creates a new Request struct.
func NewRequest() *Request {
return &Request{
JSONRPC: jsonRPCVersion,
}
}
// DecodeData decodes the given reader into the the request
// struct.
func (r *Request) DecodeData(data io.ReadCloser) error {
defer data.Close()
err := json.NewDecoder(data).Decode(r)
if err != nil {
return errors.Errorf("error parsing JSON payload: %s", err)
}
if r.JSONRPC != jsonRPCVersion {
return errors.Errorf("invalid version, expected 2.0 got: '%s'", r.JSONRPC)
}
return nil
}
// Params takes a slice of any type and attempts to bind
// the params to it.
func (r *Request) Params() (*Params, error) {
params := Params{}
err := json.Unmarshal(r.RawParams, &params)
if err != nil {
return nil, errors.Errorf("error parsing params field in payload: %s", err)
}
return &params, nil
}
// WriteErrorResponse writes an error response to the ResponseWriter.
func (s *Server) WriteErrorResponse(r *Request, w http.ResponseWriter, err error) {
jsonErr, ok := err.(*Error)
if !ok {
jsonErr = NewInternalServerError("Internal server error", err)
}
response := Response{
JSONRPC: r.JSONRPC,
Error: jsonErr,
ID: r.RawID,
}
logFields := []zap.Field{
zap.Error(jsonErr.Cause),
zap.String("method", r.Method),
}
params, err := r.Params()
if err == nil {
logFields = append(logFields, zap.Any("params", params))
}
s.log.Error("Error encountered with rpc request", logFields...)
w.WriteHeader(jsonErr.HTTPCode)
s.writeServerResponse(r, w, response)
}
// WriteResponse encodes the response and writes it to the ResponseWriter.
func (s *Server) WriteResponse(r *Request, w http.ResponseWriter, result interface{}) {
response := Response{
JSONRPC: r.JSONRPC,
Result: result,
ID: r.RawID,
}
s.writeServerResponse(r, w, response)
}
func (s *Server) writeServerResponse(r *Request, w http.ResponseWriter, response Response) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if s.config.EnableCORSWorkaround {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With")
}
encoder := json.NewEncoder(w)
err := encoder.Encode(response)
if err != nil {
s.log.Error("Error encountered while encoding response",
zap.String("err", err.Error()),
zap.String("method", r.Method))
}
}

View file

@ -1,4 +1,4 @@
package rpc package request
import ( import (
"bytes" "bytes"
@ -29,12 +29,13 @@ type (
} }
) )
// These are parameter types accepted by RPC server.
const ( const (
defaultT paramType = iota defaultT paramType = iota
stringT StringT
numberT NumberT
arrayT ArrayT
funcParamT FuncParamT
) )
func (p Param) String() string { func (p Param) String() string {
@ -123,7 +124,7 @@ func (p Param) GetBytesHex() ([]byte, error) {
func (p *Param) UnmarshalJSON(data []byte) error { func (p *Param) UnmarshalJSON(data []byte) error {
var s string var s string
if err := json.Unmarshal(data, &s); err == nil { if err := json.Unmarshal(data, &s); err == nil {
p.Type = stringT p.Type = StringT
p.Value = s p.Value = s
return nil return nil
@ -131,7 +132,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
var num float64 var num float64
if err := json.Unmarshal(data, &num); err == nil { if err := json.Unmarshal(data, &num); err == nil {
p.Type = numberT p.Type = NumberT
p.Value = int(num) p.Value = int(num)
return nil return nil
@ -142,7 +143,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
jd.DisallowUnknownFields() jd.DisallowUnknownFields()
var fp FuncParam var fp FuncParam
if err := jd.Decode(&fp); err == nil { if err := jd.Decode(&fp); err == nil {
p.Type = funcParamT p.Type = FuncParamT
p.Value = fp p.Value = fp
return nil return nil
@ -150,7 +151,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
var ps []Param var ps []Param
if err := json.Unmarshal(data, &ps); err == nil { if err := json.Unmarshal(data, &ps); err == nil {
p.Type = arrayT p.Type = ArrayT
p.Value = ps p.Value = ps
return nil return nil

View file

@ -1,4 +1,4 @@
package rpc package request
import ( import (
"encoding/hex" "encoding/hex"
@ -15,35 +15,35 @@ func TestParam_UnmarshalJSON(t *testing.T) {
msg := `["str1", 123, ["str2", 3], [{"type": "String", "value": "jajaja"}]]` msg := `["str1", 123, ["str2", 3], [{"type": "String", "value": "jajaja"}]]`
expected := Params{ expected := Params{
{ {
Type: stringT, Type: StringT,
Value: "str1", Value: "str1",
}, },
{ {
Type: numberT, Type: NumberT,
Value: 123, Value: 123,
}, },
{ {
Type: arrayT, Type: ArrayT,
Value: []Param{ Value: []Param{
{ {
Type: stringT, Type: StringT,
Value: "str2", Value: "str2",
}, },
{ {
Type: numberT, Type: NumberT,
Value: 3, Value: 3,
}, },
}, },
}, },
{ {
Type: arrayT, Type: ArrayT,
Value: []Param{ Value: []Param{
{ {
Type: funcParamT, Type: FuncParamT,
Value: FuncParam{ Value: FuncParam{
Type: String, Type: String,
Value: Param{ Value: Param{
Type: stringT, Type: StringT,
Value: "jajaja", Value: "jajaja",
}, },
}, },
@ -61,34 +61,34 @@ func TestParam_UnmarshalJSON(t *testing.T) {
} }
func TestParamGetString(t *testing.T) { func TestParamGetString(t *testing.T) {
p := Param{stringT, "jajaja"} p := Param{StringT, "jajaja"}
str, err := p.GetString() str, err := p.GetString()
assert.Equal(t, "jajaja", str) assert.Equal(t, "jajaja", str)
require.Nil(t, err) require.Nil(t, err)
p = Param{stringT, int(100500)} p = Param{StringT, int(100500)}
_, err = p.GetString() _, err = p.GetString()
require.NotNil(t, err) require.NotNil(t, err)
} }
func TestParamGetInt(t *testing.T) { func TestParamGetInt(t *testing.T) {
p := Param{numberT, int(100500)} p := Param{NumberT, int(100500)}
i, err := p.GetInt() i, err := p.GetInt()
assert.Equal(t, 100500, i) assert.Equal(t, 100500, i)
require.Nil(t, err) require.Nil(t, err)
p = Param{numberT, "jajaja"} p = Param{NumberT, "jajaja"}
_, err = p.GetInt() _, err = p.GetInt()
require.NotNil(t, err) require.NotNil(t, err)
} }
func TestParamGetArray(t *testing.T) { func TestParamGetArray(t *testing.T) {
p := Param{arrayT, []Param{{numberT, 42}}} p := Param{ArrayT, []Param{{NumberT, 42}}}
a, err := p.GetArray() a, err := p.GetArray()
assert.Equal(t, []Param{{numberT, 42}}, a) assert.Equal(t, []Param{{NumberT, 42}}, a)
require.Nil(t, err) require.Nil(t, err)
p = Param{arrayT, 42} p = Param{ArrayT, 42}
_, err = p.GetArray() _, err = p.GetArray()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -96,16 +96,16 @@ func TestParamGetArray(t *testing.T) {
func TestParamGetUint256(t *testing.T) { func TestParamGetUint256(t *testing.T) {
gas := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7" gas := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
u256, _ := util.Uint256DecodeStringLE(gas) u256, _ := util.Uint256DecodeStringLE(gas)
p := Param{stringT, gas} p := Param{StringT, gas}
u, err := p.GetUint256() u, err := p.GetUint256()
assert.Equal(t, u256, u) assert.Equal(t, u256, u)
require.Nil(t, err) require.Nil(t, err)
p = Param{stringT, 42} p = Param{StringT, 42}
_, err = p.GetUint256() _, err = p.GetUint256()
require.NotNil(t, err) require.NotNil(t, err)
p = Param{stringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"} p = Param{StringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}
_, err = p.GetUint256() _, err = p.GetUint256()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -113,16 +113,16 @@ func TestParamGetUint256(t *testing.T) {
func TestParamGetUint160FromHex(t *testing.T) { func TestParamGetUint160FromHex(t *testing.T) {
in := "50befd26fdf6e4d957c11e078b24ebce6291456f" in := "50befd26fdf6e4d957c11e078b24ebce6291456f"
u160, _ := util.Uint160DecodeStringLE(in) u160, _ := util.Uint160DecodeStringLE(in)
p := Param{stringT, in} p := Param{StringT, in}
u, err := p.GetUint160FromHex() u, err := p.GetUint160FromHex()
assert.Equal(t, u160, u) assert.Equal(t, u160, u)
require.Nil(t, err) require.Nil(t, err)
p = Param{stringT, 42} p = Param{StringT, 42}
_, err = p.GetUint160FromHex() _, err = p.GetUint160FromHex()
require.NotNil(t, err) require.NotNil(t, err)
p = Param{stringT, "wwbefd26fdf6e4d957c11e078b24ebce6291456f"} p = Param{StringT, "wwbefd26fdf6e4d957c11e078b24ebce6291456f"}
_, err = p.GetUint160FromHex() _, err = p.GetUint160FromHex()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -130,16 +130,16 @@ func TestParamGetUint160FromHex(t *testing.T) {
func TestParamGetUint160FromAddress(t *testing.T) { func TestParamGetUint160FromAddress(t *testing.T) {
in := "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y" in := "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"
u160, _ := address.StringToUint160(in) u160, _ := address.StringToUint160(in)
p := Param{stringT, in} p := Param{StringT, in}
u, err := p.GetUint160FromAddress() u, err := p.GetUint160FromAddress()
assert.Equal(t, u160, u) assert.Equal(t, u160, u)
require.Nil(t, err) require.Nil(t, err)
p = Param{stringT, 42} p = Param{StringT, 42}
_, err = p.GetUint160FromAddress() _, err = p.GetUint160FromAddress()
require.NotNil(t, err) require.NotNil(t, err)
p = Param{stringT, "QK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"} p = Param{StringT, "QK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"}
_, err = p.GetUint160FromAddress() _, err = p.GetUint160FromAddress()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -148,19 +148,19 @@ func TestParamGetFuncParam(t *testing.T) {
fp := FuncParam{ fp := FuncParam{
Type: String, Type: String,
Value: Param{ Value: Param{
Type: stringT, Type: StringT,
Value: "jajaja", Value: "jajaja",
}, },
} }
p := Param{ p := Param{
Type: funcParamT, Type: FuncParamT,
Value: fp, Value: fp,
} }
newfp, err := p.GetFuncParam() newfp, err := p.GetFuncParam()
assert.Equal(t, fp, newfp) assert.Equal(t, fp, newfp)
require.Nil(t, err) require.Nil(t, err)
p = Param{funcParamT, 42} p = Param{FuncParamT, 42}
_, err = p.GetFuncParam() _, err = p.GetFuncParam()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -168,16 +168,16 @@ func TestParamGetFuncParam(t *testing.T) {
func TestParamGetBytesHex(t *testing.T) { func TestParamGetBytesHex(t *testing.T) {
in := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7" in := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
inb, _ := hex.DecodeString(in) inb, _ := hex.DecodeString(in)
p := Param{stringT, in} p := Param{StringT, in}
bh, err := p.GetBytesHex() bh, err := p.GetBytesHex()
assert.Equal(t, inb, bh) assert.Equal(t, inb, bh)
require.Nil(t, err) require.Nil(t, err)
p = Param{stringT, 42} p = Param{StringT, 42}
_, err = p.GetBytesHex() _, err = p.GetBytesHex()
require.NotNil(t, err) require.NotNil(t, err)
p = Param{stringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"} p = Param{StringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}
_, err = p.GetBytesHex() _, err = p.GetBytesHex()
require.NotNil(t, err) require.NotNil(t, err)
} }

View file

@ -1,4 +1,4 @@
package rpc package request
type ( type (
// Params represents the JSON-RPC params. // Params represents the JSON-RPC params.

View file

@ -1,4 +1,4 @@
package rpc package request
import ( import (
"encoding/binary" "encoding/binary"

View file

@ -1,4 +1,4 @@
package rpc package request
import ( import (
"encoding/json" "encoding/json"

84
pkg/rpc/request/types.go Normal file
View file

@ -0,0 +1,84 @@
package request
import (
"encoding/json"
"io"
"github.com/pkg/errors"
)
const (
// JSONRPCVersion is the only JSON-RPC protocol version supported.
JSONRPCVersion = "2.0"
)
// RawParams is just a slice of abstract values, used to represent parameters
// passed from client to server.
type RawParams struct {
Values []interface{}
}
// NewRawParams creates RawParams from its parameters.
func NewRawParams(vals ...interface{}) RawParams {
p := RawParams{}
p.Values = make([]interface{}, len(vals))
for i := 0; i < len(p.Values); i++ {
p.Values[i] = vals[i]
}
return p
}
// Raw represents JSON-RPC request.
type Raw struct {
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
RawParams []interface{} `json:"params"`
ID int `json:"id"`
}
// In represents a standard JSON-RPC 2.0
// request: http://www.jsonrpc.org/specification#request_object. It's used in
// server to represent incoming queries.
type In struct {
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
RawParams json.RawMessage `json:"params,omitempty"`
RawID json.RawMessage `json:"id,omitempty"`
}
// NewIn creates a new Request struct.
func NewIn() *In {
return &In{
JSONRPC: JSONRPCVersion,
}
}
// DecodeData decodes the given reader into the the request
// struct.
func (r *In) DecodeData(data io.ReadCloser) error {
defer data.Close()
err := json.NewDecoder(data).Decode(r)
if err != nil {
return errors.Errorf("error parsing JSON payload: %s", err)
}
if r.JSONRPC != JSONRPCVersion {
return errors.Errorf("invalid version, expected 2.0 got: '%s'", r.JSONRPC)
}
return nil
}
// Params takes a slice of any type and attempts to bind
// the params to it.
func (r *In) Params() (*Params, error) {
params := Params{}
err := json.Unmarshal(r.RawParams, &params)
if err != nil {
return nil, errors.Errorf("error parsing params field in payload: %s", err)
}
return &params, nil
}

View file

@ -1,4 +1,4 @@
package rpc package response
import ( import (
"fmt" "fmt"
@ -18,10 +18,13 @@ type (
) )
var ( var (
errInvalidParams = NewInvalidParamsError("", nil) // ErrInvalidParams represents a generic 'invalid parameters' error.
ErrInvalidParams = NewInvalidParamsError("", nil)
) )
func newError(code int64, httpCode int, message string, data string, cause error) *Error { // NewError is an Error constructor that takes Error contents from its
// parameters.
func NewError(code int64, httpCode int, message string, data string, cause error) *Error {
return &Error{ return &Error{
Code: code, Code: code,
HTTPCode: httpCode, HTTPCode: httpCode,
@ -35,40 +38,40 @@ func newError(code int64, httpCode int, message string, data string, cause error
// NewParseError creates a new error with code // NewParseError creates a new error with code
// -32700.:%s // -32700.:%s
func NewParseError(data string, cause error) *Error { func NewParseError(data string, cause error) *Error {
return newError(-32700, http.StatusBadRequest, "Parse Error", data, cause) return NewError(-32700, http.StatusBadRequest, "Parse Error", data, cause)
} }
// NewInvalidRequestError creates a new error with // NewInvalidRequestError creates a new error with
// code -32600. // code -32600.
func NewInvalidRequestError(data string, cause error) *Error { func NewInvalidRequestError(data string, cause error) *Error {
return newError(-32600, http.StatusUnprocessableEntity, "Invalid Request", data, cause) return NewError(-32600, http.StatusUnprocessableEntity, "Invalid Request", data, cause)
} }
// NewMethodNotFoundError creates a new error with // NewMethodNotFoundError creates a new error with
// code -32601. // code -32601.
func NewMethodNotFoundError(data string, cause error) *Error { func NewMethodNotFoundError(data string, cause error) *Error {
return newError(-32601, http.StatusMethodNotAllowed, "Method not found", data, cause) return NewError(-32601, http.StatusMethodNotAllowed, "Method not found", data, cause)
} }
// NewInvalidParamsError creates a new error with // NewInvalidParamsError creates a new error with
// code -32602. // code -32602.
func NewInvalidParamsError(data string, cause error) *Error { func NewInvalidParamsError(data string, cause error) *Error {
return newError(-32602, http.StatusUnprocessableEntity, "Invalid Params", data, cause) return NewError(-32602, http.StatusUnprocessableEntity, "Invalid Params", data, cause)
} }
// NewInternalServerError creates a new error with // NewInternalServerError creates a new error with
// code -32603. // code -32603.
func NewInternalServerError(data string, cause error) *Error { func NewInternalServerError(data string, cause error) *Error {
return newError(-32603, http.StatusInternalServerError, "Internal error", data, cause) return NewError(-32603, http.StatusInternalServerError, "Internal error", data, cause)
} }
// NewRPCError creates a new error with // NewRPCError creates a new error with
// code -100 // code -100
func NewRPCError(message string, data string, cause error) *Error { func NewRPCError(message string, data string, cause error) *Error {
return newError(-100, http.StatusUnprocessableEntity, message, data, cause) return NewError(-100, http.StatusUnprocessableEntity, message, data, cause)
} }
// Error implements the error interface. // Error implements the error interface.
func (e Error) Error() string { func (e *Error) Error() string {
return fmt.Sprintf("%s (%d) - %s - %s", e.Message, e.Code, e.Data, e.Cause) return fmt.Sprintf("%s (%d) - %s - %s", e.Message, e.Code, e.Data, e.Cause)
} }

View file

@ -1,15 +1,17 @@
package rpc package response
import ( import (
"encoding/json"
"github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/rpc/response/result" "github.com/CityOfZion/neo-go/pkg/rpc/response/result"
"github.com/CityOfZion/neo-go/pkg/vm" "github.com/CityOfZion/neo-go/pkg/vm"
) )
// InvokeScriptResponse stores response for the invoke script call. // InvokeScript stores response for the invoke script call.
type InvokeScriptResponse struct { type InvokeScript struct {
responseHeader HeaderAndError
Error *Error `json:"error,omitempty"`
Result *InvokeResult `json:"result,omitempty"` Result *InvokeResult `json:"result,omitempty"`
} }
@ -19,19 +21,18 @@ type InvokeResult struct {
State vm.State `json:"state"` State vm.State `json:"state"`
GasConsumed string `json:"gas_consumed"` GasConsumed string `json:"gas_consumed"`
Script string `json:"script"` Script string `json:"script"`
Stack []StackParam Stack []request.StackParam
} }
// AccountStateResponse holds the getaccountstate response. // AccountState holds the getaccountstate response.
type AccountStateResponse struct { type AccountState struct {
responseHeader Header
Result *Account `json:"result"` Result *Account `json:"result"`
} }
// UnspentResponse represents server response to the `getunspents` command. // Unspent represents server response to the `getunspents` command.
type UnspentResponse struct { type Unspent struct {
responseHeader HeaderAndError
Error *Error `json:"error,omitempty"`
Result *result.Unspents `json:"result,omitempty"` Result *result.Unspents `json:"result,omitempty"`
} }
@ -51,68 +52,54 @@ type Balance struct {
Value string `json:"value"` Value string `json:"value"`
} }
type params struct { // Header is a generic JSON-RPC 2.0 response header (ID and JSON-RPC version).
values []interface{} type Header struct {
ID json.RawMessage `json:"id"`
JSONRPC string `json:"jsonrpc"`
} }
func newParams(vals ...interface{}) params { // HeaderAndError adds an Error (that can be empty) to the Header, it's used
p := params{} // to construct type-specific responses.
p.values = make([]interface{}, len(vals)) type HeaderAndError struct {
for i := 0; i < len(p.values); i++ { Header
p.values[i] = vals[i] Error *Error `json:"error,omitempty"`
}
return p
} }
type request struct { // Raw represents a standard raw JSON-RPC 2.0
JSONRPC string `json:"jsonrpc"` // response: http://www.jsonrpc.org/specification#response_object.
Method string `json:"method"` type Raw struct {
Params []interface{} `json:"params"` HeaderAndError
ID int `json:"id"` Result interface{} `json:"result,omitempty"`
} }
type responseHeader struct { // SendToAddress stores response for the sendtoaddress call.
ID int `json:"id"` type SendToAddress struct {
JSONRPC string `json:"jsonrpc"` HeaderAndError
Result *Tx
} }
type response struct { // GetTxOut represents result of `gettxout` RPC call.
responseHeader type GetTxOut struct {
Error *Error `json:"error"` HeaderAndError
Result interface{} `json:"result"`
}
// SendToAddressResponse stores response for the sendtoaddress call.
type SendToAddressResponse struct {
responseHeader
Error *Error `json:"error"`
Result *TxResponse
}
// GetRawTxResponse represents verbose output of `getrawtransaction` RPC call.
type GetRawTxResponse struct {
responseHeader
Error *Error `json:"error"`
Result *RawTxResponse `json:"result"`
}
// GetTxOutResponse represents result of `gettxout` RPC call.
type GetTxOutResponse struct {
responseHeader
Error *Error
Result *result.TransactionOutput Result *result.TransactionOutput
} }
// RawTxResponse stores transaction with blockchain metadata to be sent as a response. // GetRawTx represents verbose output of `getrawtransaction` RPC call.
type RawTxResponse struct { type GetRawTx struct {
TxResponse HeaderAndError
Result *RawTx `json:"result"`
}
// RawTx stores transaction with blockchain metadata to be sent as a response.
type RawTx struct {
Tx
BlockHash string `json:"blockhash"` BlockHash string `json:"blockhash"`
Confirmations uint `json:"confirmations"` Confirmations uint `json:"confirmations"`
BlockTime uint `json:"blocktime"` BlockTime uint `json:"blocktime"`
} }
// TxResponse stores transaction to be sent as a response. // Tx stores transaction to be sent as a response.
type TxResponse struct { type Tx struct {
TxID string `json:"txid"` TxID string `json:"txid"`
Size int `json:"size"` Size int `json:"size"`
Type string `json:"type"` // todo: convert to TransactionType Type string `json:"type"` // todo: convert to TransactionType

View file

@ -7,6 +7,8 @@ import (
"github.com/CityOfZion/neo-go/pkg/core" "github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/rpc/response"
"github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/smartcontract"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -17,11 +19,11 @@ import (
// missing output wrapper at the moment, thus commented out // missing output wrapper at the moment, thus commented out
// func (c *Client) getBlock(indexOrHash interface{}, verbose bool) (*response, error) { // func (c *Client) getBlock(indexOrHash interface{}, verbose bool) (*response, error) {
// var ( // var (
// params = newParams(indexOrHash) // params = request.NewRawParams(indexOrHash)
// resp = &response{} // resp = &response{}
// ) // )
// if verbose { // if verbose {
// params = newParams(indexOrHash, 1) // params = request.NewRawParams(indexOrHash, 1)
// } // }
// if err := c.performRequest("getblock", params, resp); err != nil { // if err := c.performRequest("getblock", params, resp); err != nil {
// return nil, err // return nil, err
@ -30,10 +32,10 @@ import (
// } // }
// GetAccountState returns detailed information about a NEO account. // GetAccountState returns detailed information about a NEO account.
func (c *Client) GetAccountState(address string) (*AccountStateResponse, error) { func (c *Client) GetAccountState(address string) (*response.AccountState, error) {
var ( var (
params = newParams(address) params = request.NewRawParams(address)
resp = &AccountStateResponse{} resp = &response.AccountState{}
) )
if err := c.performRequest("getaccountstate", params, resp); err != nil { if err := c.performRequest("getaccountstate", params, resp); err != nil {
return nil, err return nil, err
@ -42,10 +44,10 @@ func (c *Client) GetAccountState(address string) (*AccountStateResponse, error)
} }
// GetUnspents returns UTXOs for the given NEO account. // GetUnspents returns UTXOs for the given NEO account.
func (c *Client) GetUnspents(address string) (*UnspentResponse, error) { func (c *Client) GetUnspents(address string) (*response.Unspent, error) {
var ( var (
params = newParams(address) params = request.NewRawParams(address)
resp = &UnspentResponse{} resp = &response.Unspent{}
) )
if err := c.performRequest("getunspents", params, resp); err != nil { if err := c.performRequest("getunspents", params, resp); err != nil {
return nil, err return nil, err
@ -55,10 +57,10 @@ func (c *Client) GetUnspents(address string) (*UnspentResponse, error) {
// InvokeScript returns the result of the given script after running it true the VM. // InvokeScript returns the result of the given script after running it true the VM.
// NOTE: This is a test invoke and will not affect the blockchain. // NOTE: This is a test invoke and will not affect the blockchain.
func (c *Client) InvokeScript(script string) (*InvokeScriptResponse, error) { func (c *Client) InvokeScript(script string) (*response.InvokeScript, error) {
var ( var (
params = newParams(script) params = request.NewRawParams(script)
resp = &InvokeScriptResponse{} resp = &response.InvokeScript{}
) )
if err := c.performRequest("invokescript", params, resp); err != nil { if err := c.performRequest("invokescript", params, resp); err != nil {
return nil, err return nil, err
@ -69,10 +71,10 @@ func (c *Client) InvokeScript(script string) (*InvokeScriptResponse, error) {
// InvokeFunction returns the results after calling the smart contract scripthash // InvokeFunction returns the results after calling the smart contract scripthash
// with the given operation and parameters. // with the given operation and parameters.
// NOTE: this is test invoke and will not affect the blockchain. // NOTE: this is test invoke and will not affect the blockchain.
func (c *Client) InvokeFunction(script, operation string, params []smartcontract.Parameter) (*InvokeScriptResponse, error) { func (c *Client) InvokeFunction(script, operation string, params []smartcontract.Parameter) (*response.InvokeScript, error) {
var ( var (
p = newParams(script, operation, params) p = request.NewRawParams(script, operation, params)
resp = &InvokeScriptResponse{} resp = &response.InvokeScript{}
) )
if err := c.performRequest("invokefunction", p, resp); err != nil { if err := c.performRequest("invokefunction", p, resp); err != nil {
return nil, err return nil, err
@ -82,10 +84,10 @@ func (c *Client) InvokeFunction(script, operation string, params []smartcontract
// Invoke returns the results after calling the smart contract scripthash // Invoke returns the results after calling the smart contract scripthash
// with the given parameters. // with the given parameters.
func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*InvokeScriptResponse, error) { func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*response.InvokeScript, error) {
var ( var (
p = newParams(script, params) p = request.NewRawParams(script, params)
resp = &InvokeScriptResponse{} resp = &response.InvokeScript{}
) )
if err := c.performRequest("invoke", p, resp); err != nil { if err := c.performRequest("invoke", p, resp); err != nil {
return nil, err return nil, err
@ -97,7 +99,7 @@ func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*Invok
// missing output wrapper at the moment, thus commented out // missing output wrapper at the moment, thus commented out
// func (c *Client) getRawTransaction(hash string, verbose bool) (*response, error) { // func (c *Client) getRawTransaction(hash string, verbose bool) (*response, error) {
// var ( // var (
// params = newParams(hash, verbose) // params = request.NewRawParams(hash, verbose)
// resp = &response{} // resp = &response{}
// ) // )
// if err := c.performRequest("getrawtransaction", params, resp); err != nil { // if err := c.performRequest("getrawtransaction", params, resp); err != nil {
@ -110,10 +112,10 @@ func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*Invok
// The given hex string needs to be signed with a keypair. // The given hex string needs to be signed with a keypair.
// When the result of the response object is true, the TX has successfully // When the result of the response object is true, the TX has successfully
// been broadcasted to the network. // been broadcasted to the network.
func (c *Client) sendRawTransaction(rawTX *transaction.Transaction) (*response, error) { func (c *Client) sendRawTransaction(rawTX *transaction.Transaction) (*response.Raw, error) {
var ( var (
params = newParams(hex.EncodeToString(rawTX.Bytes())) params = request.NewRawParams(hex.EncodeToString(rawTX.Bytes()))
resp = &response{} resp = &response.Raw{}
) )
if err := c.performRequest("sendrawtransaction", params, resp); err != nil { if err := c.performRequest("sendrawtransaction", params, resp); err != nil {
return nil, err return nil, err
@ -124,7 +126,7 @@ func (c *Client) sendRawTransaction(rawTX *transaction.Transaction) (*response,
// SendToAddress sends an amount of specific asset to a given address. // SendToAddress sends an amount of specific asset to a given address.
// This call requires open wallet. (`wif` key in client struct.) // This call requires open wallet. (`wif` key in client struct.)
// If response.Result is `true` then transaction was formed correctly and was written in blockchain. // If response.Result is `true` then transaction was formed correctly and was written in blockchain.
func (c *Client) SendToAddress(asset util.Uint256, address string, amount util.Fixed8) (*SendToAddressResponse, error) { func (c *Client) SendToAddress(asset util.Uint256, address string, amount util.Fixed8) (*response.SendToAddress, error) {
var ( var (
err error err error
rawTx *transaction.Transaction rawTx *transaction.Transaction
@ -135,23 +137,23 @@ func (c *Client) SendToAddress(asset util.Uint256, address string, amount util.F
wif: c.WIF(), wif: c.WIF(),
balancer: c.Balancer(), balancer: c.Balancer(),
} }
resp *response respRaw *response.Raw
response = &SendToAddressResponse{} resp = &response.SendToAddress{}
) )
if rawTx, err = CreateRawContractTransaction(txParams); err != nil { if rawTx, err = CreateRawContractTransaction(txParams); err != nil {
return nil, errors.Wrap(err, "failed to create raw transaction for `sendtoaddress`") return nil, errors.Wrap(err, "failed to create raw transaction for `sendtoaddress`")
} }
if resp, err = c.sendRawTransaction(rawTx); err != nil { if respRaw, err = c.sendRawTransaction(rawTx); err != nil {
return nil, errors.Wrap(err, "failed to send raw transaction") return nil, errors.Wrap(err, "failed to send raw transaction")
} }
response.Error = resp.Error resp.Error = respRaw.Error
response.ID = resp.ID resp.ID = respRaw.ID
response.JSONRPC = resp.JSONRPC resp.JSONRPC = respRaw.JSONRPC
response.Result = &TxResponse{ resp.Result = &response.Tx{
TxID: rawTx.Hash().StringLE(), TxID: rawTx.Hash().StringLE(),
} }
return response, nil return resp, nil
} }
// SignAndPushInvocationTx signs and pushes given script as an invocation // SignAndPushInvocationTx signs and pushes given script as an invocation

View file

@ -1,5 +1,7 @@
package rpc package rpc
import "github.com/CityOfZion/neo-go/pkg/rpc/request"
// ContractDetails contains contract metadata. // ContractDetails contains contract metadata.
type ContractDetails struct { type ContractDetails struct {
Author string Author string
@ -10,6 +12,6 @@ type ContractDetails struct {
HasStorage bool HasStorage bool
HasDynamicInvocation bool HasDynamicInvocation bool
IsPayable bool IsPayable bool
ReturnType StackParamType ReturnType request.StackParamType
Parameters []StackParamType Parameters []request.StackParamType
} }

View file

@ -3,6 +3,7 @@ package rpc
import ( import (
"context" "context"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
@ -14,6 +15,8 @@ import (
"github.com/CityOfZion/neo-go/pkg/encoding/address" "github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/network" "github.com/CityOfZion/neo-go/pkg/network"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/rpc/response"
"github.com/CityOfZion/neo-go/pkg/rpc/response/result" "github.com/CityOfZion/neo-go/pkg/rpc/response/result"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -71,13 +74,13 @@ func (s *Server) Shutdown() error {
} }
func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request) { func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request) {
req := NewRequest() req := request.NewIn()
if httpRequest.Method != "POST" { if httpRequest.Method != "POST" {
s.WriteErrorResponse( s.WriteErrorResponse(
req, req,
w, w,
NewInvalidParamsError( response.NewInvalidParamsError(
fmt.Sprintf("Invalid method '%s', please retry with 'POST'", httpRequest.Method), nil, fmt.Sprintf("Invalid method '%s', please retry with 'POST'", httpRequest.Method), nil,
), ),
) )
@ -86,20 +89,20 @@ func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request
err := req.DecodeData(httpRequest.Body) err := req.DecodeData(httpRequest.Body)
if err != nil { if err != nil {
s.WriteErrorResponse(req, w, NewParseError("Problem parsing JSON-RPC request body", err)) s.WriteErrorResponse(req, w, response.NewParseError("Problem parsing JSON-RPC request body", err))
return return
} }
reqParams, err := req.Params() reqParams, err := req.Params()
if err != nil { if err != nil {
s.WriteErrorResponse(req, w, NewInvalidParamsError("Problem parsing request parameters", err)) s.WriteErrorResponse(req, w, response.NewInvalidParamsError("Problem parsing request parameters", err))
return return
} }
s.methodHandler(w, req, *reqParams) s.methodHandler(w, req, *reqParams)
} }
func (s *Server) methodHandler(w http.ResponseWriter, req *Request, reqParams Params) { func (s *Server) methodHandler(w http.ResponseWriter, req *request.In, reqParams request.Params) {
s.log.Debug("processing rpc request", s.log.Debug("processing rpc request",
zap.String("method", req.Method), zap.String("method", req.Method),
zap.String("params", fmt.Sprintf("%v", reqParams))) zap.String("params", fmt.Sprintf("%v", reqParams)))
@ -121,33 +124,33 @@ Methods:
param, ok := reqParams.Value(0) param, ok := reqParams.Value(0)
if !ok { if !ok {
resultsErr = errInvalidParams resultsErr = response.ErrInvalidParams
break Methods break Methods
} }
switch param.Type { switch param.Type {
case stringT: case request.StringT:
var err error var err error
hash, err = param.GetUint256() hash, err = param.GetUint256()
if err != nil { if err != nil {
resultsErr = errInvalidParams resultsErr = response.ErrInvalidParams
break Methods break Methods
} }
case numberT: case request.NumberT:
num, err := s.blockHeightFromParam(param) num, err := s.blockHeightFromParam(param)
if err != nil { if err != nil {
resultsErr = errInvalidParams resultsErr = response.ErrInvalidParams
break Methods break Methods
} }
hash = s.chain.GetHeaderHash(num) hash = s.chain.GetHeaderHash(num)
default: default:
resultsErr = errInvalidParams resultsErr = response.ErrInvalidParams
break Methods break Methods
} }
block, err := s.chain.GetBlock(hash) block, err := s.chain.GetBlock(hash)
if err != nil { if err != nil {
resultsErr = NewInternalServerError(fmt.Sprintf("Problem locating block with hash: %s", hash), err) resultsErr = response.NewInternalServerError(fmt.Sprintf("Problem locating block with hash: %s", hash), err)
break break
} }
@ -165,14 +168,14 @@ Methods:
case "getblockhash": case "getblockhash":
getblockHashCalled.Inc() getblockHashCalled.Inc()
param, ok := reqParams.ValueWithType(0, numberT) param, ok := reqParams.ValueWithType(0, request.NumberT)
if !ok { if !ok {
resultsErr = errInvalidParams resultsErr = response.ErrInvalidParams
break Methods break Methods
} }
num, err := s.blockHeightFromParam(param) num, err := s.blockHeightFromParam(param)
if err != nil { if err != nil {
resultsErr = errInvalidParams resultsErr = response.ErrInvalidParams
break Methods break Methods
} }
@ -206,22 +209,22 @@ Methods:
validateaddressCalled.Inc() validateaddressCalled.Inc()
param, ok := reqParams.Value(0) param, ok := reqParams.Value(0)
if !ok { if !ok {
resultsErr = errInvalidParams resultsErr = response.ErrInvalidParams
break Methods break Methods
} }
results = validateAddress(param.Value) results = validateAddress(param.Value)
case "getassetstate": case "getassetstate":
getassetstateCalled.Inc() getassetstateCalled.Inc()
param, ok := reqParams.ValueWithType(0, stringT) param, ok := reqParams.ValueWithType(0, request.StringT)
if !ok { if !ok {
resultsErr = errInvalidParams resultsErr = response.ErrInvalidParams
break Methods break Methods
} }
paramAssetID, err := param.GetUint256() paramAssetID, err := param.GetUint256()
if err != nil { if err != nil {
resultsErr = errInvalidParams resultsErr = response.ErrInvalidParams
break break
} }
@ -229,7 +232,7 @@ Methods:
if as != nil { if as != nil {
results = result.NewAssetState(as) results = result.NewAssetState(as)
} else { } else {
resultsErr = NewRPCError("Unknown asset", "", nil) resultsErr = response.NewRPCError("Unknown asset", "", nil)
} }
case "getaccountstate": case "getaccountstate":
@ -266,7 +269,7 @@ Methods:
results, resultsErr = s.sendrawtransaction(reqParams) results, resultsErr = s.sendrawtransaction(reqParams)
default: default:
resultsErr = NewMethodNotFoundError(fmt.Sprintf("Method '%s' not supported", req.Method), nil) resultsErr = response.NewMethodNotFoundError(fmt.Sprintf("Method '%s' not supported", req.Method), nil)
} }
if resultsErr != nil { if resultsErr != nil {
@ -277,27 +280,27 @@ Methods:
s.WriteResponse(req, w, results) s.WriteResponse(req, w, results)
} }
func (s *Server) getStorage(ps Params) (interface{}, error) { func (s *Server) getStorage(ps request.Params) (interface{}, error) {
param, ok := ps.Value(0) param, ok := ps.Value(0)
if !ok { if !ok {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
scriptHash, err := param.GetUint160FromHex() scriptHash, err := param.GetUint160FromHex()
if err != nil { if err != nil {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
scriptHash = scriptHash.Reverse() scriptHash = scriptHash.Reverse()
param, ok = ps.Value(1) param, ok = ps.Value(1)
if !ok { if !ok {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
key, err := param.GetBytesHex() key, err := param.GetBytesHex()
if err != nil { if err != nil {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
item := s.chain.GetStorageItem(scriptHash.Reverse(), key) item := s.chain.GetStorageItem(scriptHash.Reverse(), key)
@ -308,22 +311,22 @@ func (s *Server) getStorage(ps Params) (interface{}, error) {
return hex.EncodeToString(item.Value), nil return hex.EncodeToString(item.Value), nil
} }
func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) { func (s *Server) getrawtransaction(reqParams request.Params) (interface{}, error) {
var resultsErr error var resultsErr error
var results interface{} var results interface{}
if param0, ok := reqParams.Value(0); !ok { if param0, ok := reqParams.Value(0); !ok {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} else if txHash, err := param0.GetUint256(); err != nil { } else if txHash, err := param0.GetUint256(); err != nil {
resultsErr = errInvalidParams resultsErr = response.ErrInvalidParams
} else if tx, height, err := s.chain.GetTransaction(txHash); err != nil { } else if tx, height, err := s.chain.GetTransaction(txHash); err != nil {
err = errors.Wrapf(err, "Invalid transaction hash: %s", txHash) err = errors.Wrapf(err, "Invalid transaction hash: %s", txHash)
return nil, NewRPCError("Unknown transaction", err.Error(), err) return nil, response.NewRPCError("Unknown transaction", err.Error(), err)
} else if len(reqParams) >= 2 { } else if len(reqParams) >= 2 {
_header := s.chain.GetHeaderHash(int(height)) _header := s.chain.GetHeaderHash(int(height))
header, err := s.chain.GetHeader(_header) header, err := s.chain.GetHeader(_header)
if err != nil { if err != nil {
resultsErr = NewInvalidParamsError(err.Error(), err) resultsErr = response.NewInvalidParamsError(err.Error(), err)
} }
param1, _ := reqParams.Value(1) param1, _ := reqParams.Value(1)
@ -345,34 +348,34 @@ func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
return results, resultsErr return results, resultsErr
} }
func (s *Server) getTxOut(ps Params) (interface{}, error) { func (s *Server) getTxOut(ps request.Params) (interface{}, error) {
p, ok := ps.Value(0) p, ok := ps.Value(0)
if !ok { if !ok {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
h, err := p.GetUint256() h, err := p.GetUint256()
if err != nil { if err != nil {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
p, ok = ps.ValueWithType(1, numberT) p, ok = ps.ValueWithType(1, request.NumberT)
if !ok { if !ok {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
num, err := p.GetInt() num, err := p.GetInt()
if err != nil || num < 0 { if err != nil || num < 0 {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
tx, _, err := s.chain.GetTransaction(h) tx, _, err := s.chain.GetTransaction(h)
if err != nil { if err != nil {
return nil, NewInvalidParamsError(err.Error(), err) return nil, response.NewInvalidParamsError(err.Error(), err)
} }
if num >= len(tx.Outputs) { if num >= len(tx.Outputs) {
return nil, NewInvalidParamsError("invalid index", errors.New("too big index")) return nil, response.NewInvalidParamsError("invalid index", errors.New("too big index"))
} }
out := tx.Outputs[num] out := tx.Outputs[num]
@ -380,35 +383,35 @@ func (s *Server) getTxOut(ps Params) (interface{}, error) {
} }
// getContractState returns contract state (contract information, according to the contract script hash). // getContractState returns contract state (contract information, according to the contract script hash).
func (s *Server) getContractState(reqParams Params) (interface{}, error) { func (s *Server) getContractState(reqParams request.Params) (interface{}, error) {
var results interface{} var results interface{}
param, ok := reqParams.ValueWithType(0, stringT) param, ok := reqParams.ValueWithType(0, request.StringT)
if !ok { if !ok {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} else if scriptHash, err := param.GetUint160FromHex(); err != nil { } else if scriptHash, err := param.GetUint160FromHex(); err != nil {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} else { } else {
cs := s.chain.GetContractState(scriptHash) cs := s.chain.GetContractState(scriptHash)
if cs != nil { if cs != nil {
results = result.NewContractState(cs) results = result.NewContractState(cs)
} else { } else {
return nil, NewRPCError("Unknown contract", "", nil) return nil, response.NewRPCError("Unknown contract", "", nil)
} }
} }
return results, nil return results, nil
} }
// getAccountState returns account state either in short or full (unspents included) form. // getAccountState returns account state either in short or full (unspents included) form.
func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{}, error) { func (s *Server) getAccountState(reqParams request.Params, unspents bool) (interface{}, error) {
var resultsErr error var resultsErr error
var results interface{} var results interface{}
param, ok := reqParams.ValueWithType(0, stringT) param, ok := reqParams.ValueWithType(0, request.StringT)
if !ok { if !ok {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} else if scriptHash, err := param.GetUint160FromAddress(); err != nil { } else if scriptHash, err := param.GetUint160FromAddress(); err != nil {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} else { } else {
as := s.chain.GetAccountState(scriptHash) as := s.chain.GetAccountState(scriptHash)
if as == nil { if as == nil {
@ -417,7 +420,7 @@ func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{},
if unspents { if unspents {
str, err := param.GetString() str, err := param.GetString()
if err != nil { if err != nil {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
results = result.NewUnspents(as, s.chain, str) results = result.NewUnspents(as, s.chain, str)
} else { } else {
@ -428,18 +431,18 @@ func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{},
} }
// invoke implements the `invoke` RPC call. // invoke implements the `invoke` RPC call.
func (s *Server) invoke(reqParams Params) (interface{}, error) { func (s *Server) invoke(reqParams request.Params) (interface{}, error) {
scriptHashHex, ok := reqParams.ValueWithType(0, stringT) scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT)
if !ok { if !ok {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
scriptHash, err := scriptHashHex.GetUint160FromHex() scriptHash, err := scriptHashHex.GetUint160FromHex()
if err != nil { if err != nil {
return nil, err return nil, err
} }
sliceP, ok := reqParams.ValueWithType(1, arrayT) sliceP, ok := reqParams.ValueWithType(1, request.ArrayT)
if !ok { if !ok {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
slice, err := sliceP.GetArray() slice, err := sliceP.GetArray()
if err != nil { if err != nil {
@ -453,10 +456,10 @@ func (s *Server) invoke(reqParams Params) (interface{}, error) {
} }
// invokescript implements the `invokescript` RPC call. // invokescript implements the `invokescript` RPC call.
func (s *Server) invokeFunction(reqParams Params) (interface{}, error) { func (s *Server) invokeFunction(reqParams request.Params) (interface{}, error) {
scriptHashHex, ok := reqParams.ValueWithType(0, stringT) scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT)
if !ok { if !ok {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
scriptHash, err := scriptHashHex.GetUint160FromHex() scriptHash, err := scriptHashHex.GetUint160FromHex()
if err != nil { if err != nil {
@ -470,14 +473,14 @@ func (s *Server) invokeFunction(reqParams Params) (interface{}, error) {
} }
// invokescript implements the `invokescript` RPC call. // invokescript implements the `invokescript` RPC call.
func (s *Server) invokescript(reqParams Params) (interface{}, error) { func (s *Server) invokescript(reqParams request.Params) (interface{}, error) {
if len(reqParams) < 1 { if len(reqParams) < 1 {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
script, err := reqParams[0].GetBytesHex() script, err := reqParams[0].GetBytesHex()
if err != nil { if err != nil {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} }
return s.runScriptInVM(script), nil return s.runScriptInVM(script), nil
@ -499,14 +502,14 @@ func (s *Server) runScriptInVM(script []byte) *result.Invoke {
return result return result
} }
func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) { func (s *Server) sendrawtransaction(reqParams request.Params) (interface{}, error) {
var resultsErr error var resultsErr error
var results interface{} var results interface{}
if len(reqParams) < 1 { if len(reqParams) < 1 {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} else if byteTx, err := reqParams[0].GetBytesHex(); err != nil { } else if byteTx, err := reqParams[0].GetBytesHex(); err != nil {
return nil, errInvalidParams return nil, response.ErrInvalidParams
} else { } else {
r := io.NewBinReaderFromBuf(byteTx) r := io.NewBinReaderFromBuf(byteTx)
tx := &transaction.Transaction{} tx := &transaction.Transaction{}
@ -533,14 +536,14 @@ func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) {
} }
} }
if err != nil { if err != nil {
resultsErr = NewInternalServerError(err.Error(), err) resultsErr = response.NewInternalServerError(err.Error(), err)
} }
} }
return results, resultsErr return results, resultsErr
} }
func (s *Server) blockHeightFromParam(param *Param) (int, error) { func (s *Server) blockHeightFromParam(param *request.Param) (int, error) {
num, err := param.GetInt() num, err := param.GetInt()
if err != nil { if err != nil {
return 0, nil return 0, nil
@ -552,6 +555,71 @@ func (s *Server) blockHeightFromParam(param *Param) (int, error) {
return num, nil return num, nil
} }
// WriteErrorResponse writes an error response to the ResponseWriter.
func (s *Server) WriteErrorResponse(r *request.In, w http.ResponseWriter, err error) {
jsonErr, ok := err.(*response.Error)
if !ok {
jsonErr = response.NewInternalServerError("Internal server error", err)
}
resp := response.Raw{
HeaderAndError: response.HeaderAndError{
Header: response.Header{
JSONRPC: r.JSONRPC,
ID: r.RawID,
},
Error: jsonErr,
},
}
logFields := []zap.Field{
zap.Error(jsonErr.Cause),
zap.String("method", r.Method),
}
params, err := r.Params()
if err == nil {
logFields = append(logFields, zap.Any("params", params))
}
s.log.Error("Error encountered with rpc request", logFields...)
w.WriteHeader(jsonErr.HTTPCode)
s.writeServerResponse(r, w, resp)
}
// WriteResponse encodes the response and writes it to the ResponseWriter.
func (s *Server) WriteResponse(r *request.In, w http.ResponseWriter, result interface{}) {
resp := response.Raw{
HeaderAndError: response.HeaderAndError{
Header: response.Header{
JSONRPC: r.JSONRPC,
ID: r.RawID,
},
},
Result: result,
}
s.writeServerResponse(r, w, resp)
}
func (s *Server) writeServerResponse(r *request.In, w http.ResponseWriter, resp response.Raw) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if s.config.EnableCORSWorkaround {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With")
}
encoder := json.NewEncoder(w)
err := encoder.Encode(resp)
if err != nil {
s.log.Error("Error encountered while encoding response",
zap.String("err", err.Error()),
zap.String("method", r.Method))
}
}
// validateAddress verifies that the address is a correct NEO address // validateAddress verifies that the address is a correct NEO address
// see https://docs.neo.org/en-us/node/cli/2.9.4/api/validateaddress.html // see https://docs.neo.org/en-us/node/cli/2.9.4/api/validateaddress.html
func validateAddress(addr interface{}) result.ValidateAddress { func validateAddress(addr interface{}) result.ValidateAddress {

View file

@ -11,6 +11,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/storage"
"github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/network" "github.com/CityOfZion/neo-go/pkg/network"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/rpc/response/result" "github.com/CityOfZion/neo-go/pkg/rpc/response/result"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -38,11 +39,11 @@ type SendTXResponse struct {
type InvokeFunctionResponse struct { type InvokeFunctionResponse struct {
Jsonrpc string `json:"jsonrpc"` Jsonrpc string `json:"jsonrpc"`
Result struct { Result struct {
Script string `json:"script"` Script string `json:"script"`
State string `json:"state"` State string `json:"state"`
GasConsumed string `json:"gas_consumed"` GasConsumed string `json:"gas_consumed"`
Stack []FuncParam `json:"stack"` Stack []request.FuncParam `json:"stack"`
TX string `json:"tx,omitempty"` TX string `json:"tx,omitempty"`
} `json:"result"` } `json:"result"`
ID int `json:"id"` ID int `json:"id"`
} }

View file

@ -14,6 +14,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/core" "github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/rpc/response"
"github.com/CityOfZion/neo-go/pkg/rpc/response/result" "github.com/CityOfZion/neo-go/pkg/rpc/response/result"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -602,7 +603,7 @@ func TestRPC(t *testing.T) {
body := doRPCCall(rpc, handler, t) body := doRPCCall(rpc, handler, t)
checkErrResponse(t, body, false) checkErrResponse(t, body, false)
var result GetTxOutResponse var result response.GetTxOut
err := json.Unmarshal(body, &result) err := json.Unmarshal(body, &result)
require.NoErrorf(t, err, "could not parse response: %s", body) require.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, 0, result.Result.N) assert.Equal(t, 0, result.Result.N)

View file

@ -9,6 +9,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/encoding/address" "github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/smartcontract"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm/emit" "github.com/CityOfZion/neo-go/pkg/vm/emit"
@ -141,38 +142,38 @@ func CreateDeploymentScript(avm []byte, contract *ContractDetails) ([]byte, erro
// expandArrayIntoScript pushes all FuncParam parameters from the given array // expandArrayIntoScript pushes all FuncParam parameters from the given array
// into the given buffer in reverse order. // into the given buffer in reverse order.
func expandArrayIntoScript(script *io.BinWriter, slice []Param) error { func expandArrayIntoScript(script *io.BinWriter, slice []request.Param) error {
for j := len(slice) - 1; j >= 0; j-- { for j := len(slice) - 1; j >= 0; j-- {
fp, err := slice[j].GetFuncParam() fp, err := slice[j].GetFuncParam()
if err != nil { if err != nil {
return err return err
} }
switch fp.Type { switch fp.Type {
case ByteArray, Signature: case request.ByteArray, request.Signature:
str, err := fp.Value.GetBytesHex() str, err := fp.Value.GetBytesHex()
if err != nil { if err != nil {
return err return err
} }
emit.Bytes(script, str) emit.Bytes(script, str)
case String: case request.String:
str, err := fp.Value.GetString() str, err := fp.Value.GetString()
if err != nil { if err != nil {
return err return err
} }
emit.String(script, str) emit.String(script, str)
case Hash160: case request.Hash160:
hash, err := fp.Value.GetUint160FromHex() hash, err := fp.Value.GetUint160FromHex()
if err != nil { if err != nil {
return err return err
} }
emit.Bytes(script, hash.BytesBE()) emit.Bytes(script, hash.BytesBE())
case Hash256: case request.Hash256:
hash, err := fp.Value.GetUint256() hash, err := fp.Value.GetUint256()
if err != nil { if err != nil {
return err return err
} }
emit.Bytes(script, hash.BytesBE()) emit.Bytes(script, hash.BytesBE())
case PublicKey: case request.PublicKey:
str, err := fp.Value.GetString() str, err := fp.Value.GetString()
if err != nil { if err != nil {
return err return err
@ -182,13 +183,13 @@ func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
return err return err
} }
emit.Bytes(script, key.Bytes()) emit.Bytes(script, key.Bytes())
case Integer: case request.Integer:
val, err := fp.Value.GetInt() val, err := fp.Value.GetInt()
if err != nil { if err != nil {
return err return err
} }
emit.Int(script, int64(val)) emit.Int(script, int64(val))
case Boolean: case request.Boolean:
str, err := fp.Value.GetString() str, err := fp.Value.GetString()
if err != nil { if err != nil {
return err return err
@ -210,19 +211,19 @@ func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
// CreateFunctionInvocationScript creates a script to invoke given contract with // CreateFunctionInvocationScript creates a script to invoke given contract with
// given parameters. // given parameters.
func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byte, error) { func CreateFunctionInvocationScript(contract util.Uint160, params request.Params) ([]byte, error) {
script := io.NewBufBinWriter() script := io.NewBufBinWriter()
for i := len(params) - 1; i >= 0; i-- { for i := len(params) - 1; i >= 0; i-- {
switch params[i].Type { switch params[i].Type {
case stringT: case request.StringT:
emit.String(script.BinWriter, params[i].String()) emit.String(script.BinWriter, params[i].String())
case numberT: case request.NumberT:
num, err := params[i].GetInt() num, err := params[i].GetInt()
if err != nil { if err != nil {
return nil, err return nil, err
} }
emit.String(script.BinWriter, strconv.Itoa(num)) emit.String(script.BinWriter, strconv.Itoa(num))
case arrayT: case request.ArrayT:
slice, err := params[i].GetArray() slice, err := params[i].GetArray()
if err != nil { if err != nil {
return nil, err return nil, err
@ -244,7 +245,7 @@ func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byt
// given parameters. It differs from CreateFunctionInvocationScript in that it // given parameters. It differs from CreateFunctionInvocationScript in that it
// expects one array of FuncParams and expands it onto the stack as independent // expects one array of FuncParams and expands it onto the stack as independent
// elements. // elements.
func CreateInvocationScript(contract util.Uint160, funcParams []Param) ([]byte, error) { func CreateInvocationScript(contract util.Uint160, funcParams []request.Param) ([]byte, error) {
script := io.NewBufBinWriter() script := io.NewBufBinWriter()
err := expandArrayIntoScript(script.BinWriter, funcParams) err := expandArrayIntoScript(script.BinWriter, funcParams)
if err != nil { if err != nil {

View file

@ -4,56 +4,57 @@ import (
"encoding/hex" "encoding/hex"
"testing" "testing"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestInvocationScriptCreationGood(t *testing.T) { func TestInvocationScriptCreationGood(t *testing.T) {
p := Param{stringT, "50befd26fdf6e4d957c11e078b24ebce6291456f"} p := request.Param{Type: request.StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}
contract, err := p.GetUint160FromHex() contract, err := p.GetUint160FromHex()
require.Nil(t, err) require.Nil(t, err)
var paramScripts = []struct { var paramScripts = []struct {
ps Params ps request.Params
script string script string
}{{ }{{
script: "676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "transfer"}}, ps: request.Params{{Type: request.StringT, Value: "transfer"}},
script: "087472616e73666572676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "087472616e73666572676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{numberT, 42}}, ps: request.Params{{Type: request.NumberT, Value: 42}},
script: "023432676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "023432676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "a"}, {arrayT, []Param{}}}, ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{}}},
script: "00c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "00c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{ByteArray, Param{stringT, "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.ByteArray, Value: request.Param{Type: request.StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
script: "1450befd26fdf6e4d957c11e078b24ebce6291456f51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "1450befd26fdf6e4d957c11e078b24ebce6291456f51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Signature, Param{stringT, "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}}}}}}, ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Signature, Value: request.Param{Type: request.StringT, Value: "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}}}}}},
script: "404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{String, Param{stringT, "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.String, Value: request.Param{Type: request.StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
script: "283530626566643236666466366534643935376331316530373862323465626365363239313435366651c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "283530626566643236666466366534643935376331316530373862323465626365363239313435366651c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Hash160, Param{stringT, "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Hash160, Value: request.Param{Type: request.StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
script: "146f459162ceeb248b071ec157d9e4f6fd26fdbe5051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "146f459162ceeb248b071ec157d9e4f6fd26fdbe5051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Hash256, Param{stringT, "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}}}}}}, ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Hash256, Value: request.Param{Type: request.StringT, Value: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}}}}}},
script: "20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{PublicKey, Param{stringT, "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}}}}}}, ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.PublicKey, Value: request.Param{Type: request.StringT, Value: "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}}}}}},
script: "2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c151c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c151c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Integer, Param{numberT, 42}}}}}}, ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Integer, Value: request.Param{Type: request.NumberT, Value: 42}}}}}},
script: "012a51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "012a51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Boolean, Param{stringT, "true"}}}}}}, ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Boolean, Value: request.Param{Type: request.StringT, Value: "true"}}}}}},
script: "5151c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "5151c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, { }, {
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Boolean, Param{stringT, "false"}}}}}}, ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Boolean, Value: request.Param{Type: request.StringT, Value: "false"}}}}}},
script: "0051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}} }}
for _, ps := range paramScripts { for _, ps := range paramScripts {
@ -66,21 +67,21 @@ func TestInvocationScriptCreationGood(t *testing.T) {
func TestInvocationScriptCreationBad(t *testing.T) { func TestInvocationScriptCreationBad(t *testing.T) {
contract := util.Uint160{} contract := util.Uint160{}
var testParams = []Params{ var testParams = []request.Params{
{{numberT, "qwerty"}}, {{Type: request.NumberT, Value: "qwerty"}},
{{arrayT, 42}}, {{Type: request.ArrayT, Value: 42}},
{{arrayT, []Param{{numberT, 42}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.NumberT, Value: 42}}}},
{{arrayT, []Param{{funcParamT, FuncParam{ByteArray, Param{stringT, "qwerty"}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.ByteArray, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Signature, Param{stringT, "qwerty"}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Signature, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{String, Param{numberT, 42}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.String, Value: request.Param{Type: request.NumberT, Value: 42}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Hash160, Param{stringT, "qwerty"}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Hash160, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Hash256, Param{stringT, "qwerty"}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Hash256, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{PublicKey, Param{numberT, 42}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.PublicKey, Value: request.Param{Type: request.NumberT, Value: 42}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{PublicKey, Param{stringT, "qwerty"}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.PublicKey, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Integer, Param{stringT, "qwerty"}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Integer, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Boolean, Param{numberT, 42}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Boolean, Value: request.Param{Type: request.NumberT, Value: 42}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Boolean, Param{stringT, "qwerty"}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Boolean, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Unknown, Param{}}}}}}, {{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Unknown, Value: request.Param{}}}}}},
} }
for _, ps := range testParams { for _, ps := range testParams {
_, err := CreateFunctionInvocationScript(contract, ps) _, err := CreateFunctionInvocationScript(contract, ps)