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/keys"
"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/util"
"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.
if !ctx.Bool("skip-details") {
details := parseContractDetails()
details.ReturnType = rpc.ByteArray
details.Parameters = make([]rpc.StackParamType, 2)
details.Parameters[0] = rpc.String
details.Parameters[1] = rpc.Array
details.ReturnType = request.ByteArray
details.Parameters = make([]request.StackParamType, 2)
details.Parameters[0] = request.String
details.Parameters[1] = request.Array
project := &ProjectConfig{Contract: details}
b, err := yaml.Marshal(project)
@ -362,7 +364,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
operation string
params = make([]smartcontract.Parameter, 0)
paramsStart = 1
resp *rpc.InvokeScriptResponse
resp *response.InvokeScript
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/transaction"
"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/pkg/errors"
)
@ -177,12 +178,12 @@ 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 (
r = request{
r = request.Raw{
JSONRPC: c.version,
Method: method,
Params: p.values,
RawParams: p.Values,
ID: 1,
}
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 (
"bytes"
@ -29,12 +29,13 @@ type (
}
)
// These are parameter types accepted by RPC server.
const (
defaultT paramType = iota
stringT
numberT
arrayT
funcParamT
StringT
NumberT
ArrayT
FuncParamT
)
func (p Param) String() string {
@ -123,7 +124,7 @@ func (p Param) GetBytesHex() ([]byte, error) {
func (p *Param) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err == nil {
p.Type = stringT
p.Type = StringT
p.Value = s
return nil
@ -131,7 +132,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
var num float64
if err := json.Unmarshal(data, &num); err == nil {
p.Type = numberT
p.Type = NumberT
p.Value = int(num)
return nil
@ -142,7 +143,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
jd.DisallowUnknownFields()
var fp FuncParam
if err := jd.Decode(&fp); err == nil {
p.Type = funcParamT
p.Type = FuncParamT
p.Value = fp
return nil
@ -150,7 +151,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
var ps []Param
if err := json.Unmarshal(data, &ps); err == nil {
p.Type = arrayT
p.Type = ArrayT
p.Value = ps
return nil

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
package rpc
package request
import (
"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 (
"fmt"
@ -18,10 +18,13 @@ type (
)
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{
Code: code,
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
// -32700.:%s
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
// code -32600.
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
// code -32601.
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
// code -32602.
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
// code -32603.
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
// code -100
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.
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)
}

View file

@ -1,15 +1,17 @@
package rpc
package response
import (
"encoding/json"
"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/vm"
)
// InvokeScriptResponse stores response for the invoke script call.
type InvokeScriptResponse struct {
responseHeader
Error *Error `json:"error,omitempty"`
// InvokeScript stores response for the invoke script call.
type InvokeScript struct {
HeaderAndError
Result *InvokeResult `json:"result,omitempty"`
}
@ -19,19 +21,18 @@ type InvokeResult struct {
State vm.State `json:"state"`
GasConsumed string `json:"gas_consumed"`
Script string `json:"script"`
Stack []StackParam
Stack []request.StackParam
}
// AccountStateResponse holds the getaccountstate response.
type AccountStateResponse struct {
responseHeader
// AccountState holds the getaccountstate response.
type AccountState struct {
Header
Result *Account `json:"result"`
}
// UnspentResponse represents server response to the `getunspents` command.
type UnspentResponse struct {
responseHeader
Error *Error `json:"error,omitempty"`
// Unspent represents server response to the `getunspents` command.
type Unspent struct {
HeaderAndError
Result *result.Unspents `json:"result,omitempty"`
}
@ -51,68 +52,54 @@ type Balance struct {
Value string `json:"value"`
}
type params struct {
values []interface{}
}
func newParams(vals ...interface{}) params {
p := params{}
p.values = make([]interface{}, len(vals))
for i := 0; i < len(p.values); i++ {
p.values[i] = vals[i]
}
return p
}
type request struct {
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
Params []interface{} `json:"params"`
ID int `json:"id"`
}
type responseHeader struct {
ID int `json:"id"`
// Header is a generic JSON-RPC 2.0 response header (ID and JSON-RPC version).
type Header struct {
ID json.RawMessage `json:"id"`
JSONRPC string `json:"jsonrpc"`
}
type response struct {
responseHeader
Error *Error `json:"error"`
Result interface{} `json:"result"`
// HeaderAndError adds an Error (that can be empty) to the Header, it's used
// to construct type-specific responses.
type HeaderAndError struct {
Header
Error *Error `json:"error,omitempty"`
}
// SendToAddressResponse stores response for the sendtoaddress call.
type SendToAddressResponse struct {
responseHeader
Error *Error `json:"error"`
Result *TxResponse
// Raw represents a standard raw JSON-RPC 2.0
// response: http://www.jsonrpc.org/specification#response_object.
type Raw struct {
HeaderAndError
Result interface{} `json:"result,omitempty"`
}
// GetRawTxResponse represents verbose output of `getrawtransaction` RPC call.
type GetRawTxResponse struct {
responseHeader
Error *Error `json:"error"`
Result *RawTxResponse `json:"result"`
// SendToAddress stores response for the sendtoaddress call.
type SendToAddress struct {
HeaderAndError
Result *Tx
}
// GetTxOutResponse represents result of `gettxout` RPC call.
type GetTxOutResponse struct {
responseHeader
Error *Error
// GetTxOut represents result of `gettxout` RPC call.
type GetTxOut struct {
HeaderAndError
Result *result.TransactionOutput
}
// RawTxResponse stores transaction with blockchain metadata to be sent as a response.
type RawTxResponse struct {
TxResponse
// GetRawTx represents verbose output of `getrawtransaction` RPC call.
type GetRawTx struct {
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"`
Confirmations uint `json:"confirmations"`
BlockTime uint `json:"blocktime"`
}
// TxResponse stores transaction to be sent as a response.
type TxResponse struct {
// Tx stores transaction to be sent as a response.
type Tx struct {
TxID string `json:"txid"`
Size int `json:"size"`
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/transaction"
"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/util"
"github.com/pkg/errors"
@ -17,11 +19,11 @@ import (
// missing output wrapper at the moment, thus commented out
// func (c *Client) getBlock(indexOrHash interface{}, verbose bool) (*response, error) {
// var (
// params = newParams(indexOrHash)
// params = request.NewRawParams(indexOrHash)
// resp = &response{}
// )
// if verbose {
// params = newParams(indexOrHash, 1)
// params = request.NewRawParams(indexOrHash, 1)
// }
// if err := c.performRequest("getblock", params, resp); err != nil {
// return nil, err
@ -30,10 +32,10 @@ import (
// }
// 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 (
params = newParams(address)
resp = &AccountStateResponse{}
params = request.NewRawParams(address)
resp = &response.AccountState{}
)
if err := c.performRequest("getaccountstate", params, resp); err != nil {
return nil, err
@ -42,10 +44,10 @@ func (c *Client) GetAccountState(address string) (*AccountStateResponse, error)
}
// 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 (
params = newParams(address)
resp = &UnspentResponse{}
params = request.NewRawParams(address)
resp = &response.Unspent{}
)
if err := c.performRequest("getunspents", params, resp); err != nil {
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.
// 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 (
params = newParams(script)
resp = &InvokeScriptResponse{}
params = request.NewRawParams(script)
resp = &response.InvokeScript{}
)
if err := c.performRequest("invokescript", params, resp); err != nil {
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
// with the given operation and parameters.
// 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 (
p = newParams(script, operation, params)
resp = &InvokeScriptResponse{}
p = request.NewRawParams(script, operation, params)
resp = &response.InvokeScript{}
)
if err := c.performRequest("invokefunction", p, resp); err != nil {
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
// 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 (
p = newParams(script, params)
resp = &InvokeScriptResponse{}
p = request.NewRawParams(script, params)
resp = &response.InvokeScript{}
)
if err := c.performRequest("invoke", p, resp); err != nil {
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
// func (c *Client) getRawTransaction(hash string, verbose bool) (*response, error) {
// var (
// params = newParams(hash, verbose)
// params = request.NewRawParams(hash, verbose)
// resp = &response{}
// )
// 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.
// When the result of the response object is true, the TX has successfully
// 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 (
params = newParams(hex.EncodeToString(rawTX.Bytes()))
resp = &response{}
params = request.NewRawParams(hex.EncodeToString(rawTX.Bytes()))
resp = &response.Raw{}
)
if err := c.performRequest("sendrawtransaction", params, resp); err != nil {
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.
// 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.
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 (
err error
rawTx *transaction.Transaction
@ -135,23 +137,23 @@ func (c *Client) SendToAddress(asset util.Uint256, address string, amount util.F
wif: c.WIF(),
balancer: c.Balancer(),
}
resp *response
response = &SendToAddressResponse{}
respRaw *response.Raw
resp = &response.SendToAddress{}
)
if rawTx, err = CreateRawContractTransaction(txParams); err != nil {
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")
}
response.Error = resp.Error
response.ID = resp.ID
response.JSONRPC = resp.JSONRPC
response.Result = &TxResponse{
resp.Error = respRaw.Error
resp.ID = respRaw.ID
resp.JSONRPC = respRaw.JSONRPC
resp.Result = &response.Tx{
TxID: rawTx.Hash().StringLE(),
}
return response, nil
return resp, nil
}
// SignAndPushInvocationTx signs and pushes given script as an invocation

View file

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

View file

@ -3,6 +3,7 @@ package rpc
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"strconv"
@ -14,6 +15,8 @@ import (
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io"
"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/util"
"github.com/pkg/errors"
@ -71,13 +74,13 @@ func (s *Server) Shutdown() error {
}
func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request) {
req := NewRequest()
req := request.NewIn()
if httpRequest.Method != "POST" {
s.WriteErrorResponse(
req,
w,
NewInvalidParamsError(
response.NewInvalidParamsError(
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)
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
}
reqParams, err := req.Params()
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
}
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",
zap.String("method", req.Method),
zap.String("params", fmt.Sprintf("%v", reqParams)))
@ -121,33 +124,33 @@ Methods:
param, ok := reqParams.Value(0)
if !ok {
resultsErr = errInvalidParams
resultsErr = response.ErrInvalidParams
break Methods
}
switch param.Type {
case stringT:
case request.StringT:
var err error
hash, err = param.GetUint256()
if err != nil {
resultsErr = errInvalidParams
resultsErr = response.ErrInvalidParams
break Methods
}
case numberT:
case request.NumberT:
num, err := s.blockHeightFromParam(param)
if err != nil {
resultsErr = errInvalidParams
resultsErr = response.ErrInvalidParams
break Methods
}
hash = s.chain.GetHeaderHash(num)
default:
resultsErr = errInvalidParams
resultsErr = response.ErrInvalidParams
break Methods
}
block, err := s.chain.GetBlock(hash)
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
}
@ -165,14 +168,14 @@ Methods:
case "getblockhash":
getblockHashCalled.Inc()
param, ok := reqParams.ValueWithType(0, numberT)
param, ok := reqParams.ValueWithType(0, request.NumberT)
if !ok {
resultsErr = errInvalidParams
resultsErr = response.ErrInvalidParams
break Methods
}
num, err := s.blockHeightFromParam(param)
if err != nil {
resultsErr = errInvalidParams
resultsErr = response.ErrInvalidParams
break Methods
}
@ -206,22 +209,22 @@ Methods:
validateaddressCalled.Inc()
param, ok := reqParams.Value(0)
if !ok {
resultsErr = errInvalidParams
resultsErr = response.ErrInvalidParams
break Methods
}
results = validateAddress(param.Value)
case "getassetstate":
getassetstateCalled.Inc()
param, ok := reqParams.ValueWithType(0, stringT)
param, ok := reqParams.ValueWithType(0, request.StringT)
if !ok {
resultsErr = errInvalidParams
resultsErr = response.ErrInvalidParams
break Methods
}
paramAssetID, err := param.GetUint256()
if err != nil {
resultsErr = errInvalidParams
resultsErr = response.ErrInvalidParams
break
}
@ -229,7 +232,7 @@ Methods:
if as != nil {
results = result.NewAssetState(as)
} else {
resultsErr = NewRPCError("Unknown asset", "", nil)
resultsErr = response.NewRPCError("Unknown asset", "", nil)
}
case "getaccountstate":
@ -266,7 +269,7 @@ Methods:
results, resultsErr = s.sendrawtransaction(reqParams)
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 {
@ -277,27 +280,27 @@ Methods:
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)
if !ok {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
scriptHash, err := param.GetUint160FromHex()
if err != nil {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
scriptHash = scriptHash.Reverse()
param, ok = ps.Value(1)
if !ok {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
key, err := param.GetBytesHex()
if err != nil {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
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
}
func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
func (s *Server) getrawtransaction(reqParams request.Params) (interface{}, error) {
var resultsErr error
var results interface{}
if param0, ok := reqParams.Value(0); !ok {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
} else if txHash, err := param0.GetUint256(); err != nil {
resultsErr = errInvalidParams
resultsErr = response.ErrInvalidParams
} else if tx, height, err := s.chain.GetTransaction(txHash); err != nil {
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 {
_header := s.chain.GetHeaderHash(int(height))
header, err := s.chain.GetHeader(_header)
if err != nil {
resultsErr = NewInvalidParamsError(err.Error(), err)
resultsErr = response.NewInvalidParamsError(err.Error(), err)
}
param1, _ := reqParams.Value(1)
@ -345,34 +348,34 @@ func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
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)
if !ok {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
h, err := p.GetUint256()
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 {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
num, err := p.GetInt()
if err != nil || num < 0 {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
tx, _, err := s.chain.GetTransaction(h)
if err != nil {
return nil, NewInvalidParamsError(err.Error(), err)
return nil, response.NewInvalidParamsError(err.Error(), err)
}
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]
@ -380,35 +383,35 @@ func (s *Server) getTxOut(ps Params) (interface{}, error) {
}
// 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{}
param, ok := reqParams.ValueWithType(0, stringT)
param, ok := reqParams.ValueWithType(0, request.StringT)
if !ok {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
} else if scriptHash, err := param.GetUint160FromHex(); err != nil {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
} else {
cs := s.chain.GetContractState(scriptHash)
if cs != nil {
results = result.NewContractState(cs)
} else {
return nil, NewRPCError("Unknown contract", "", nil)
return nil, response.NewRPCError("Unknown contract", "", nil)
}
}
return results, nil
}
// 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 results interface{}
param, ok := reqParams.ValueWithType(0, stringT)
param, ok := reqParams.ValueWithType(0, request.StringT)
if !ok {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
} else if scriptHash, err := param.GetUint160FromAddress(); err != nil {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
} else {
as := s.chain.GetAccountState(scriptHash)
if as == nil {
@ -417,7 +420,7 @@ func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{},
if unspents {
str, err := param.GetString()
if err != nil {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
results = result.NewUnspents(as, s.chain, str)
} else {
@ -428,18 +431,18 @@ func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{},
}
// invoke implements the `invoke` RPC call.
func (s *Server) invoke(reqParams Params) (interface{}, error) {
scriptHashHex, ok := reqParams.ValueWithType(0, stringT)
func (s *Server) invoke(reqParams request.Params) (interface{}, error) {
scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT)
if !ok {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
scriptHash, err := scriptHashHex.GetUint160FromHex()
if err != nil {
return nil, err
}
sliceP, ok := reqParams.ValueWithType(1, arrayT)
sliceP, ok := reqParams.ValueWithType(1, request.ArrayT)
if !ok {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
slice, err := sliceP.GetArray()
if err != nil {
@ -453,10 +456,10 @@ func (s *Server) invoke(reqParams Params) (interface{}, error) {
}
// invokescript implements the `invokescript` RPC call.
func (s *Server) invokeFunction(reqParams Params) (interface{}, error) {
scriptHashHex, ok := reqParams.ValueWithType(0, stringT)
func (s *Server) invokeFunction(reqParams request.Params) (interface{}, error) {
scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT)
if !ok {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
scriptHash, err := scriptHashHex.GetUint160FromHex()
if err != nil {
@ -470,14 +473,14 @@ func (s *Server) invokeFunction(reqParams Params) (interface{}, error) {
}
// 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 {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
script, err := reqParams[0].GetBytesHex()
if err != nil {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
}
return s.runScriptInVM(script), nil
@ -499,14 +502,14 @@ func (s *Server) runScriptInVM(script []byte) *result.Invoke {
return result
}
func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) {
func (s *Server) sendrawtransaction(reqParams request.Params) (interface{}, error) {
var resultsErr error
var results interface{}
if len(reqParams) < 1 {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
} else if byteTx, err := reqParams[0].GetBytesHex(); err != nil {
return nil, errInvalidParams
return nil, response.ErrInvalidParams
} else {
r := io.NewBinReaderFromBuf(byteTx)
tx := &transaction.Transaction{}
@ -533,14 +536,14 @@ func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) {
}
}
if err != nil {
resultsErr = NewInternalServerError(err.Error(), err)
resultsErr = response.NewInternalServerError(err.Error(), err)
}
}
return results, resultsErr
}
func (s *Server) blockHeightFromParam(param *Param) (int, error) {
func (s *Server) blockHeightFromParam(param *request.Param) (int, error) {
num, err := param.GetInt()
if err != nil {
return 0, nil
@ -552,6 +555,71 @@ func (s *Server) blockHeightFromParam(param *Param) (int, error) {
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
// see https://docs.neo.org/en-us/node/cli/2.9.4/api/validateaddress.html
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/io"
"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/util"
"github.com/stretchr/testify/require"
@ -41,7 +42,7 @@ type InvokeFunctionResponse struct {
Script string `json:"script"`
State string `json:"state"`
GasConsumed string `json:"gas_consumed"`
Stack []FuncParam `json:"stack"`
Stack []request.FuncParam `json:"stack"`
TX string `json:"tx,omitempty"`
} `json:"result"`
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/transaction"
"github.com/CityOfZion/neo-go/pkg/rpc/response"
"github.com/CityOfZion/neo-go/pkg/rpc/response/result"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/stretchr/testify/assert"
@ -602,7 +603,7 @@ func TestRPC(t *testing.T) {
body := doRPCCall(rpc, handler, t)
checkErrResponse(t, body, false)
var result GetTxOutResponse
var result response.GetTxOut
err := json.Unmarshal(body, &result)
require.NoErrorf(t, err, "could not parse response: %s", body)
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/encoding/address"
"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/util"
"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
// 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-- {
fp, err := slice[j].GetFuncParam()
if err != nil {
return err
}
switch fp.Type {
case ByteArray, Signature:
case request.ByteArray, request.Signature:
str, err := fp.Value.GetBytesHex()
if err != nil {
return err
}
emit.Bytes(script, str)
case String:
case request.String:
str, err := fp.Value.GetString()
if err != nil {
return err
}
emit.String(script, str)
case Hash160:
case request.Hash160:
hash, err := fp.Value.GetUint160FromHex()
if err != nil {
return err
}
emit.Bytes(script, hash.BytesBE())
case Hash256:
case request.Hash256:
hash, err := fp.Value.GetUint256()
if err != nil {
return err
}
emit.Bytes(script, hash.BytesBE())
case PublicKey:
case request.PublicKey:
str, err := fp.Value.GetString()
if err != nil {
return err
@ -182,13 +183,13 @@ func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
return err
}
emit.Bytes(script, key.Bytes())
case Integer:
case request.Integer:
val, err := fp.Value.GetInt()
if err != nil {
return err
}
emit.Int(script, int64(val))
case Boolean:
case request.Boolean:
str, err := fp.Value.GetString()
if err != nil {
return err
@ -210,19 +211,19 @@ func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
// CreateFunctionInvocationScript creates a script to invoke given contract with
// given parameters.
func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byte, error) {
func CreateFunctionInvocationScript(contract util.Uint160, params request.Params) ([]byte, error) {
script := io.NewBufBinWriter()
for i := len(params) - 1; i >= 0; i-- {
switch params[i].Type {
case stringT:
case request.StringT:
emit.String(script.BinWriter, params[i].String())
case numberT:
case request.NumberT:
num, err := params[i].GetInt()
if err != nil {
return nil, err
}
emit.String(script.BinWriter, strconv.Itoa(num))
case arrayT:
case request.ArrayT:
slice, err := params[i].GetArray()
if err != nil {
return nil, err
@ -244,7 +245,7 @@ func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byt
// given parameters. It differs from CreateFunctionInvocationScript in that it
// expects one array of FuncParams and expands it onto the stack as independent
// elements.
func CreateInvocationScript(contract util.Uint160, funcParams []Param) ([]byte, error) {
func CreateInvocationScript(contract util.Uint160, funcParams []request.Param) ([]byte, error) {
script := io.NewBufBinWriter()
err := expandArrayIntoScript(script.BinWriter, funcParams)
if err != nil {

View file

@ -4,56 +4,57 @@ import (
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestInvocationScriptCreationGood(t *testing.T) {
p := Param{stringT, "50befd26fdf6e4d957c11e078b24ebce6291456f"}
p := request.Param{Type: request.StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}
contract, err := p.GetUint160FromHex()
require.Nil(t, err)
var paramScripts = []struct {
ps Params
ps request.Params
script string
}{{
script: "676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, {
ps: Params{{stringT, "transfer"}},
ps: request.Params{{Type: request.StringT, Value: "transfer"}},
script: "087472616e73666572676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, {
ps: Params{{numberT, 42}},
ps: request.Params{{Type: request.NumberT, Value: 42}},
script: "023432676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
}, {
ps: Params{{stringT, "a"}, {arrayT, []Param{}}},
ps: request.Params{{Type: request.StringT, Value: "a"}, {Type: request.ArrayT, Value: []request.Param{}}},
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",
}, {
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",
}, {
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",
}, {
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",
}, {
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",
}, {
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",
}, {
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",
}, {
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",
}, {
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",
}}
for _, ps := range paramScripts {
@ -66,21 +67,21 @@ func TestInvocationScriptCreationGood(t *testing.T) {
func TestInvocationScriptCreationBad(t *testing.T) {
contract := util.Uint160{}
var testParams = []Params{
{{numberT, "qwerty"}},
{{arrayT, 42}},
{{arrayT, []Param{{numberT, 42}}}},
{{arrayT, []Param{{funcParamT, FuncParam{ByteArray, Param{stringT, "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Signature, Param{stringT, "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{String, Param{numberT, 42}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Hash160, Param{stringT, "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Hash256, Param{stringT, "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{PublicKey, Param{numberT, 42}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{PublicKey, Param{stringT, "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Integer, Param{stringT, "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Boolean, Param{numberT, 42}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Boolean, Param{stringT, "qwerty"}}}}}},
{{arrayT, []Param{{funcParamT, FuncParam{Unknown, Param{}}}}}},
var testParams = []request.Params{
{{Type: request.NumberT, Value: "qwerty"}},
{{Type: request.ArrayT, Value: 42}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.NumberT, Value: 42}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.ByteArray, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Signature, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.String, Value: request.Param{Type: request.NumberT, Value: 42}}}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Hash160, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Hash256, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.PublicKey, Value: request.Param{Type: request.NumberT, Value: 42}}}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.PublicKey, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Integer, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Boolean, Value: request.Param{Type: request.NumberT, Value: 42}}}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Boolean, Value: request.Param{Type: request.StringT, Value: "qwerty"}}}}}},
{{Type: request.ArrayT, Value: []request.Param{{Type: request.FuncParamT, Value: request.FuncParam{Type: request.Unknown, Value: request.Param{}}}}}},
}
for _, ps := range testParams {
_, err := CreateFunctionInvocationScript(contract, ps)