2020-01-14 12:02:38 +00:00
|
|
|
package request
|
2018-03-23 20:36:59 +00:00
|
|
|
|
|
|
|
import (
|
2019-11-26 10:13:17 +00:00
|
|
|
"bytes"
|
2020-07-07 19:35:03 +00:00
|
|
|
"encoding/base64"
|
2019-11-21 14:42:02 +00:00
|
|
|
"encoding/hex"
|
2019-11-21 13:42:51 +00:00
|
|
|
"encoding/json"
|
2018-03-23 20:36:59 +00:00
|
|
|
"fmt"
|
2020-03-25 14:23:13 +00:00
|
|
|
"strconv"
|
2019-11-21 13:42:51 +00:00
|
|
|
|
2020-06-10 11:45:55 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2019-11-21 13:42:51 +00:00
|
|
|
"github.com/pkg/errors"
|
2018-03-23 20:36:59 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2019-10-22 14:56:03 +00:00
|
|
|
// Param represents a param either passed to
|
2018-03-23 20:36:59 +00:00
|
|
|
// the server or to send to a server using
|
|
|
|
// the client.
|
|
|
|
Param struct {
|
2019-11-21 13:42:51 +00:00
|
|
|
Type paramType
|
|
|
|
Value interface{}
|
2018-03-23 20:36:59 +00:00
|
|
|
}
|
2019-11-21 13:42:51 +00:00
|
|
|
|
|
|
|
paramType int
|
2019-11-26 10:13:17 +00:00
|
|
|
// FuncParam represents a function argument parameter used in the
|
|
|
|
// invokefunction RPC method.
|
|
|
|
FuncParam struct {
|
2020-02-21 14:34:18 +00:00
|
|
|
Type smartcontract.ParamType `json:"type"`
|
2020-03-03 14:22:15 +00:00
|
|
|
Value Param `json:"value"`
|
2019-11-26 10:13:17 +00:00
|
|
|
}
|
2020-05-13 10:16:42 +00:00
|
|
|
// BlockFilter is a wrapper structure for block event filter. The only
|
|
|
|
// allowed filter is primary index.
|
|
|
|
BlockFilter struct {
|
|
|
|
Primary int `json:"primary"`
|
|
|
|
}
|
|
|
|
// TxFilter is a wrapper structure for transaction event filter. It
|
|
|
|
// allows to filter transactions by senders and cosigners.
|
|
|
|
TxFilter struct {
|
|
|
|
Sender *util.Uint160 `json:"sender,omitempty"`
|
|
|
|
Cosigner *util.Uint160 `json:"cosigner,omitempty"`
|
|
|
|
}
|
|
|
|
// NotificationFilter is a wrapper structure representing filter used for
|
|
|
|
// notifications generated during transaction execution. Notifications can
|
|
|
|
// only be filtered by contract hash.
|
|
|
|
NotificationFilter struct {
|
|
|
|
Contract util.Uint160 `json:"contract"`
|
|
|
|
}
|
|
|
|
// ExecutionFilter is a wrapper structure used for transaction execution
|
|
|
|
// events. It allows to choose failing or successful transactions based
|
|
|
|
// on their VM state.
|
|
|
|
ExecutionFilter struct {
|
|
|
|
State string `json:"state"`
|
|
|
|
}
|
2019-11-21 13:42:51 +00:00
|
|
|
)
|
|
|
|
|
2020-01-14 12:02:38 +00:00
|
|
|
// These are parameter types accepted by RPC server.
|
2019-11-21 13:42:51 +00:00
|
|
|
const (
|
|
|
|
defaultT paramType = iota
|
2020-01-14 12:02:38 +00:00
|
|
|
StringT
|
|
|
|
NumberT
|
|
|
|
ArrayT
|
|
|
|
FuncParamT
|
2020-05-13 10:16:42 +00:00
|
|
|
BlockFilterT
|
|
|
|
TxFilterT
|
|
|
|
NotificationFilterT
|
|
|
|
ExecutionFilterT
|
2020-06-10 11:45:55 +00:00
|
|
|
Cosigner
|
2018-03-23 20:36:59 +00:00
|
|
|
)
|
|
|
|
|
2020-06-04 11:58:47 +00:00
|
|
|
var errMissingParameter = errors.New("parameter is missing")
|
|
|
|
|
2018-03-23 20:36:59 +00:00
|
|
|
func (p Param) String() string {
|
2019-11-21 13:42:51 +00:00
|
|
|
return fmt.Sprintf("%v", p.Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetString returns string value of the parameter.
|
2020-06-04 11:58:47 +00:00
|
|
|
func (p *Param) GetString() (string, error) {
|
|
|
|
if p == nil {
|
|
|
|
return "", errMissingParameter
|
|
|
|
}
|
2019-11-26 10:13:17 +00:00
|
|
|
str, ok := p.Value.(string)
|
|
|
|
if !ok {
|
|
|
|
return "", errors.New("not a string")
|
|
|
|
}
|
|
|
|
return str, nil
|
|
|
|
}
|
2019-11-21 13:42:51 +00:00
|
|
|
|
2020-06-04 12:43:37 +00:00
|
|
|
// GetBoolean returns boolean value of the parameter.
|
|
|
|
func (p *Param) GetBoolean() bool {
|
|
|
|
if p == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
switch p.Type {
|
|
|
|
case NumberT:
|
|
|
|
return p.Value != 0
|
|
|
|
case StringT:
|
|
|
|
return p.Value != ""
|
|
|
|
default:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 13:42:51 +00:00
|
|
|
// GetInt returns int value of te parameter.
|
2020-06-04 11:58:47 +00:00
|
|
|
func (p *Param) GetInt() (int, error) {
|
|
|
|
if p == nil {
|
|
|
|
return 0, errMissingParameter
|
|
|
|
}
|
2019-11-26 10:13:17 +00:00
|
|
|
i, ok := p.Value.(int)
|
2020-03-25 14:23:13 +00:00
|
|
|
if ok {
|
|
|
|
return i, nil
|
|
|
|
} else if s, ok := p.Value.(string); ok {
|
|
|
|
return strconv.Atoi(s)
|
2019-11-26 10:13:17 +00:00
|
|
|
}
|
2020-03-25 14:23:13 +00:00
|
|
|
return 0, errors.New("not an integer")
|
2019-11-26 10:13:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetArray returns a slice of Params stored in the parameter.
|
2020-06-04 11:58:47 +00:00
|
|
|
func (p *Param) GetArray() ([]Param, error) {
|
|
|
|
if p == nil {
|
|
|
|
return nil, errMissingParameter
|
|
|
|
}
|
2019-11-26 10:13:17 +00:00
|
|
|
a, ok := p.Value.([]Param)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("not an array")
|
|
|
|
}
|
|
|
|
return a, nil
|
|
|
|
}
|
2019-11-21 13:42:51 +00:00
|
|
|
|
|
|
|
// GetUint256 returns Uint256 value of the parameter.
|
2020-06-04 11:58:47 +00:00
|
|
|
func (p *Param) GetUint256() (util.Uint256, error) {
|
2019-11-26 10:13:17 +00:00
|
|
|
s, err := p.GetString()
|
|
|
|
if err != nil {
|
|
|
|
return util.Uint256{}, err
|
2019-11-21 13:42:51 +00:00
|
|
|
}
|
|
|
|
|
2019-11-27 09:23:18 +00:00
|
|
|
return util.Uint256DecodeStringLE(s)
|
2019-11-21 13:42:51 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 10:13:17 +00:00
|
|
|
// GetUint160FromHex returns Uint160 value of the parameter encoded in hex.
|
2020-06-04 11:58:47 +00:00
|
|
|
func (p *Param) GetUint160FromHex() (util.Uint160, error) {
|
2019-11-26 10:13:17 +00:00
|
|
|
s, err := p.GetString()
|
|
|
|
if err != nil {
|
|
|
|
return util.Uint160{}, err
|
|
|
|
}
|
2020-03-13 13:47:08 +00:00
|
|
|
if len(s) == 2*util.Uint160Size+2 && s[0] == '0' && s[1] == 'x' {
|
|
|
|
s = s[2:]
|
|
|
|
}
|
2019-11-26 10:13:17 +00:00
|
|
|
|
2019-12-06 16:47:58 +00:00
|
|
|
return util.Uint160DecodeStringLE(s)
|
2019-11-26 10:13:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetUint160FromAddress returns Uint160 value of the parameter that was
|
|
|
|
// supplied as an address.
|
2020-06-04 11:58:47 +00:00
|
|
|
func (p *Param) GetUint160FromAddress() (util.Uint160, error) {
|
2019-11-26 10:13:17 +00:00
|
|
|
s, err := p.GetString()
|
|
|
|
if err != nil {
|
|
|
|
return util.Uint160{}, err
|
|
|
|
}
|
|
|
|
|
2019-12-25 14:34:18 +00:00
|
|
|
return address.StringToUint160(s)
|
2019-11-26 10:13:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetFuncParam returns current parameter as a function call parameter.
|
2020-06-04 11:58:47 +00:00
|
|
|
func (p *Param) GetFuncParam() (FuncParam, error) {
|
|
|
|
if p == nil {
|
|
|
|
return FuncParam{}, errMissingParameter
|
|
|
|
}
|
2019-11-26 10:13:17 +00:00
|
|
|
fp, ok := p.Value.(FuncParam)
|
|
|
|
if !ok {
|
|
|
|
return FuncParam{}, errors.New("not a function parameter")
|
|
|
|
}
|
|
|
|
return fp, nil
|
|
|
|
}
|
|
|
|
|
2019-11-21 14:42:02 +00:00
|
|
|
// GetBytesHex returns []byte value of the parameter if
|
|
|
|
// it is a hex-encoded string.
|
2020-06-04 11:58:47 +00:00
|
|
|
func (p *Param) GetBytesHex() ([]byte, error) {
|
2019-11-26 10:13:17 +00:00
|
|
|
s, err := p.GetString()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-11-21 14:42:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return hex.DecodeString(s)
|
|
|
|
}
|
|
|
|
|
2020-07-07 19:35:03 +00:00
|
|
|
// GetBytesBase64 returns []byte value of the parameter if
|
|
|
|
// it is a base64-encoded string.
|
|
|
|
func (p *Param) GetBytesBase64() ([]byte, error) {
|
|
|
|
s, err := p.GetString()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return base64.StdEncoding.DecodeString(s)
|
|
|
|
}
|
|
|
|
|
2020-06-10 11:45:55 +00:00
|
|
|
// GetCosigner returns transaction.Cosigner value of the parameter.
|
|
|
|
func (p Param) GetCosigner() (transaction.Cosigner, error) {
|
|
|
|
c, ok := p.Value.(transaction.Cosigner)
|
|
|
|
if !ok {
|
|
|
|
return transaction.Cosigner{}, errors.New("not a cosigner")
|
|
|
|
}
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCosigners returns a slice of transaction.Cosigner with global scope from
|
|
|
|
// array of Uint160 or array of serialized transaction.Cosigner stored in the
|
|
|
|
// parameter.
|
|
|
|
func (p Param) GetCosigners() ([]transaction.Cosigner, error) {
|
|
|
|
hashes, err := p.GetArray()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cosigners := make([]transaction.Cosigner, len(hashes))
|
|
|
|
// try to extract hashes first
|
|
|
|
for i, h := range hashes {
|
|
|
|
var u util.Uint160
|
|
|
|
u, err = h.GetUint160FromHex()
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
cosigners[i] = transaction.Cosigner{
|
|
|
|
Account: u,
|
|
|
|
Scopes: transaction.Global,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
for i, h := range hashes {
|
|
|
|
cosigners[i], err = h.GetCosigner()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cosigners, nil
|
|
|
|
}
|
|
|
|
|
2019-11-21 13:42:51 +00:00
|
|
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
|
|
|
func (p *Param) UnmarshalJSON(data []byte) error {
|
|
|
|
var s string
|
|
|
|
var num float64
|
2020-05-13 10:16:42 +00:00
|
|
|
// To unmarshal correctly we need to pass pointers into the decoder.
|
|
|
|
var attempts = [...]Param{
|
|
|
|
{NumberT, &num},
|
|
|
|
{StringT, &s},
|
|
|
|
{FuncParamT, &FuncParam{}},
|
|
|
|
{BlockFilterT, &BlockFilter{}},
|
|
|
|
{TxFilterT, &TxFilter{}},
|
|
|
|
{NotificationFilterT, &NotificationFilter{}},
|
|
|
|
{ExecutionFilterT, &ExecutionFilter{}},
|
2020-06-10 11:45:55 +00:00
|
|
|
{Cosigner, &transaction.Cosigner{}},
|
2020-05-13 10:16:42 +00:00
|
|
|
{ArrayT, &[]Param{}},
|
2019-11-26 10:13:17 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 13:38:44 +00:00
|
|
|
if bytes.Equal(data, []byte("null")) {
|
|
|
|
p.Type = defaultT
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-13 10:16:42 +00:00
|
|
|
for _, cur := range attempts {
|
|
|
|
r := bytes.NewReader(data)
|
|
|
|
jd := json.NewDecoder(r)
|
|
|
|
jd.DisallowUnknownFields()
|
|
|
|
if err := jd.Decode(cur.Value); err == nil {
|
|
|
|
p.Type = cur.Type
|
|
|
|
// But we need to store actual values, not pointers.
|
|
|
|
switch val := cur.Value.(type) {
|
|
|
|
case *float64:
|
|
|
|
p.Value = int(*val)
|
|
|
|
case *string:
|
|
|
|
p.Value = *val
|
|
|
|
case *FuncParam:
|
|
|
|
p.Value = *val
|
|
|
|
case *BlockFilter:
|
|
|
|
p.Value = *val
|
|
|
|
case *TxFilter:
|
|
|
|
p.Value = *val
|
|
|
|
case *NotificationFilter:
|
|
|
|
p.Value = *val
|
|
|
|
case *ExecutionFilter:
|
2020-05-13 14:13:33 +00:00
|
|
|
if (*val).State == "HALT" || (*val).State == "FAULT" {
|
|
|
|
p.Value = *val
|
|
|
|
} else {
|
|
|
|
continue
|
|
|
|
}
|
2020-06-10 11:45:55 +00:00
|
|
|
case *transaction.Cosigner:
|
|
|
|
p.Value = *val
|
2020-05-13 10:16:42 +00:00
|
|
|
case *[]Param:
|
|
|
|
p.Value = *val
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-11-21 14:42:02 +00:00
|
|
|
}
|
|
|
|
|
2019-11-21 13:42:51 +00:00
|
|
|
return errors.New("unknown type")
|
2018-03-23 20:36:59 +00:00
|
|
|
}
|