rpc: *In parameters marshalling optimisation

Parse request parameters on-demand.
This commit is contained in:
Anna Shaleva 2021-10-28 14:10:18 +03:00
parent 1a32fcb2dc
commit 2e8bbf2a87
11 changed files with 444 additions and 482 deletions

View file

@ -1649,8 +1649,8 @@ func wrapInitResponse(r *request.In, resp string) string {
case "getversion": case "getversion":
response = `{"id":1,"jsonrpc":"2.0","result":{"network":42,"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}` response = `{"id":1,"jsonrpc":"2.0","result":{"network":42,"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`
case "getcontractstate": case "getcontractstate":
p, _ := r.Params() p := request.Params(r.RawParams)
name, _ := p.ValueWithType(0, request.StringT).GetString() name, _ := p.Value(0).GetString()
switch name { switch name {
case "NeoToken": case "NeoToken":
response = `{"id":1,"jsonrpc":"2.0","result":{"id":-1,"script":"DANORU9Ba2d4Cw==","manifest":{"name":"NEO","abi":{"hash":"0xde5f57d430d3dece511cf975a8d37848cb9e0525","methods":[{"name":"name","offset":0,"parameters":null,"returntype":"String"},{"name":"symbol","offset":0,"parameters":null,"returntype":"String"},{"name":"decimals","offset":0,"parameters":null,"returntype":"Integer"},{"name":"totalSupply","offset":0,"parameters":null,"returntype":"Integer"},{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer"},{"name":"transfer","offset":0,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}],"returntype":"Boolean"},{"name":"onPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"postPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"unclaimedGas","offset":0,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer"},{"name":"registerCandidate","offset":0,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"unregisterCandidate","offset":0,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"vote","offset":0,"parameters":[{"name":"account","type":"Hash160"},{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"getCandidates","offset":0,"parameters":null,"returntype":"Array"},{"name":"getСommittee","offset":0,"parameters":null,"returntype":"Array"},{"name":"getNextBlockValidators","offset":0,"parameters":null,"returntype":"Array"},{"name":"getGasPerBlock","offset":0,"parameters":null,"returntype":"Integer"},{"name":"setGasPerBlock","offset":0,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Boolean"}],"events":[{"name":"Transfer","parameters":null}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-5"],"trusts":[],"safemethods":["name","symbol","decimals","totalSupply","balanceOf","unclaimedGas","getCandidates","getСommittee","getNextBlockValidators"],"extra":null},"hash":"0xde5f57d430d3dece511cf975a8d37848cb9e0525"}}` response = `{"id":1,"jsonrpc":"2.0","result":{"id":-1,"script":"DANORU9Ba2d4Cw==","manifest":{"name":"NEO","abi":{"hash":"0xde5f57d430d3dece511cf975a8d37848cb9e0525","methods":[{"name":"name","offset":0,"parameters":null,"returntype":"String"},{"name":"symbol","offset":0,"parameters":null,"returntype":"String"},{"name":"decimals","offset":0,"parameters":null,"returntype":"Integer"},{"name":"totalSupply","offset":0,"parameters":null,"returntype":"Integer"},{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer"},{"name":"transfer","offset":0,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}],"returntype":"Boolean"},{"name":"onPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"postPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"unclaimedGas","offset":0,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer"},{"name":"registerCandidate","offset":0,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"unregisterCandidate","offset":0,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"vote","offset":0,"parameters":[{"name":"account","type":"Hash160"},{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"getCandidates","offset":0,"parameters":null,"returntype":"Array"},{"name":"getСommittee","offset":0,"parameters":null,"returntype":"Array"},{"name":"getNextBlockValidators","offset":0,"parameters":null,"returntype":"Array"},{"name":"getGasPerBlock","offset":0,"parameters":null,"returntype":"Integer"},{"name":"setGasPerBlock","offset":0,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Boolean"}],"events":[{"name":"Transfer","parameters":null}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-5"],"trusts":[],"safemethods":["name","symbol","decimals","totalSupply","balanceOf","unclaimedGas","getCandidates","getСommittee","getNextBlockValidators"],"extra":null},"hash":"0xde5f57d430d3dece511cf975a8d37848cb9e0525"}}`

View file

@ -134,9 +134,7 @@ readloop:
// Bad event received. // Bad event received.
break break
} }
var slice []json.RawMessage if event != response.MissedEventID && len(rr.RawParams) != 1 {
err = json.Unmarshal(rr.RawParams, &slice)
if err != nil || (event != response.MissedEventID && len(slice) != 1) {
// Bad event received. // Bad event received.
break break
} }
@ -159,7 +157,7 @@ readloop:
break readloop break readloop
} }
if event != response.MissedEventID { if event != response.MissedEventID {
err = json.Unmarshal(slice[0], val) err = json.Unmarshal(rr.RawParams[0].RawMessage, val)
if err != nil { if err != nil {
// Bad event received. // Bad event received.
break break

View file

@ -187,10 +187,8 @@ func TestWSFilteredSubscriptions(t *testing.T) {
}, },
func(t *testing.T, p *request.Params) { func(t *testing.T, p *request.Params) {
param := p.Value(1) param := p.Value(1)
raw, ok := param.Value.(json.RawMessage)
require.True(t, ok)
filt := new(request.BlockFilter) filt := new(request.BlockFilter)
require.NoError(t, json.Unmarshal(raw, filt)) require.NoError(t, json.Unmarshal(param.RawMessage, filt))
require.Equal(t, 3, filt.Primary) require.Equal(t, 3, filt.Primary)
}, },
}, },
@ -202,10 +200,8 @@ func TestWSFilteredSubscriptions(t *testing.T) {
}, },
func(t *testing.T, p *request.Params) { func(t *testing.T, p *request.Params) {
param := p.Value(1) param := p.Value(1)
raw, ok := param.Value.(json.RawMessage)
require.True(t, ok)
filt := new(request.TxFilter) filt := new(request.TxFilter)
require.NoError(t, json.Unmarshal(raw, filt)) require.NoError(t, json.Unmarshal(param.RawMessage, filt))
require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Sender) require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Sender)
require.Nil(t, filt.Signer) require.Nil(t, filt.Signer)
}, },
@ -218,10 +214,8 @@ func TestWSFilteredSubscriptions(t *testing.T) {
}, },
func(t *testing.T, p *request.Params) { func(t *testing.T, p *request.Params) {
param := p.Value(1) param := p.Value(1)
raw, ok := param.Value.(json.RawMessage)
require.True(t, ok)
filt := new(request.TxFilter) filt := new(request.TxFilter)
require.NoError(t, json.Unmarshal(raw, filt)) require.NoError(t, json.Unmarshal(param.RawMessage, filt))
require.Nil(t, filt.Sender) require.Nil(t, filt.Sender)
require.Equal(t, util.Uint160{0, 42}, *filt.Signer) require.Equal(t, util.Uint160{0, 42}, *filt.Signer)
}, },
@ -235,10 +229,8 @@ func TestWSFilteredSubscriptions(t *testing.T) {
}, },
func(t *testing.T, p *request.Params) { func(t *testing.T, p *request.Params) {
param := p.Value(1) param := p.Value(1)
raw, ok := param.Value.(json.RawMessage)
require.True(t, ok)
filt := new(request.TxFilter) filt := new(request.TxFilter)
require.NoError(t, json.Unmarshal(raw, filt)) require.NoError(t, json.Unmarshal(param.RawMessage, filt))
require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Sender) require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Sender)
require.Equal(t, util.Uint160{0, 42}, *filt.Signer) require.Equal(t, util.Uint160{0, 42}, *filt.Signer)
}, },
@ -251,10 +243,8 @@ func TestWSFilteredSubscriptions(t *testing.T) {
}, },
func(t *testing.T, p *request.Params) { func(t *testing.T, p *request.Params) {
param := p.Value(1) param := p.Value(1)
raw, ok := param.Value.(json.RawMessage)
require.True(t, ok)
filt := new(request.NotificationFilter) filt := new(request.NotificationFilter)
require.NoError(t, json.Unmarshal(raw, filt)) require.NoError(t, json.Unmarshal(param.RawMessage, filt))
require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Contract) require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Contract)
require.Nil(t, filt.Name) require.Nil(t, filt.Name)
}, },
@ -267,10 +257,8 @@ func TestWSFilteredSubscriptions(t *testing.T) {
}, },
func(t *testing.T, p *request.Params) { func(t *testing.T, p *request.Params) {
param := p.Value(1) param := p.Value(1)
raw, ok := param.Value.(json.RawMessage)
require.True(t, ok)
filt := new(request.NotificationFilter) filt := new(request.NotificationFilter)
require.NoError(t, json.Unmarshal(raw, filt)) require.NoError(t, json.Unmarshal(param.RawMessage, filt))
require.Equal(t, "my_pretty_notification", *filt.Name) require.Equal(t, "my_pretty_notification", *filt.Name)
require.Nil(t, filt.Contract) require.Nil(t, filt.Contract)
}, },
@ -284,10 +272,8 @@ func TestWSFilteredSubscriptions(t *testing.T) {
}, },
func(t *testing.T, p *request.Params) { func(t *testing.T, p *request.Params) {
param := p.Value(1) param := p.Value(1)
raw, ok := param.Value.(json.RawMessage)
require.True(t, ok)
filt := new(request.NotificationFilter) filt := new(request.NotificationFilter)
require.NoError(t, json.Unmarshal(raw, filt)) require.NoError(t, json.Unmarshal(param.RawMessage, filt))
require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Contract) require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Contract)
require.Equal(t, "my_pretty_notification", *filt.Name) require.Equal(t, "my_pretty_notification", *filt.Name)
}, },
@ -300,10 +286,8 @@ func TestWSFilteredSubscriptions(t *testing.T) {
}, },
func(t *testing.T, p *request.Params) { func(t *testing.T, p *request.Params) {
param := p.Value(1) param := p.Value(1)
raw, ok := param.Value.(json.RawMessage)
require.True(t, ok)
filt := new(request.ExecutionFilter) filt := new(request.ExecutionFilter)
require.NoError(t, json.Unmarshal(raw, filt)) require.NoError(t, json.Unmarshal(param.RawMessage, filt))
require.Equal(t, "FAULT", filt.State) require.Equal(t, "FAULT", filt.State)
}, },
}, },
@ -320,9 +304,8 @@ func TestWSFilteredSubscriptions(t *testing.T) {
req := request.In{} req := request.In{}
err = ws.ReadJSON(&req) err = ws.ReadJSON(&req)
require.NoError(t, err) require.NoError(t, err)
params, err := req.Params() params := request.Params(req.RawParams)
require.NoError(t, err) c.serverCode(t, &params)
c.serverCode(t, params)
err = ws.SetWriteDeadline(time.Now().Add(2 * time.Second)) err = ws.SetWriteDeadline(time.Now().Add(2 * time.Second))
require.NoError(t, err) require.NoError(t, err)
err = ws.WriteMessage(1, []byte(`{"jsonrpc": "2.0", "id": 1, "result": "0"}`)) err = ws.WriteMessage(1, []byte(`{"jsonrpc": "2.0", "id": 1, "result": "0"}`))

View file

@ -22,11 +22,9 @@ type (
// the server or to send to a server using // the server or to send to a server using
// the client. // the client.
Param struct { Param struct {
Type paramType json.RawMessage
Value interface{}
} }
paramType int
// FuncParam represents a function argument parameter used in the // FuncParam represents a function argument parameter used in the
// invokefunction RPC method. // invokefunction RPC method.
FuncParam struct { FuncParam struct {
@ -64,68 +62,151 @@ type (
} }
) )
// These are parameter types accepted by RPC server. var (
const ( jsonNullBytes = []byte("null")
defaultT paramType = iota jsonFalseBytes = []byte("false")
StringT jsonTrueBytes = []byte("true")
NumberT errMissingParameter = errors.New("parameter is missing")
BooleanT errNotAString = errors.New("not a string")
ArrayT errNotAnInt = errors.New("not an integer")
FuncParamT errNotABool = errors.New("not a boolean")
BlockFilterT errNotAnArray = errors.New("not an array")
TxFilterT
NotificationFilterT
ExecutionFilterT
SignerWithWitnessT
) )
var errMissingParameter = errors.New("parameter is missing")
func (p Param) String() string { func (p Param) String() string {
return fmt.Sprintf("%v", p.Value) str, _ := p.GetString()
return str
} }
// GetString returns string value of the parameter. // GetStringStrict returns string value of the parameter.
func (p *Param) GetStringStrict() (string, error) {
if p == nil {
return "", errMissingParameter
}
if p.IsNull() {
return "", errNotAString
}
var s string
err := json.Unmarshal(p.RawMessage, &s)
if err != nil {
return "", errNotAString
}
return s, nil
}
// GetString returns string value of the parameter or tries to cast parameter to a string value.
func (p *Param) GetString() (string, error) { func (p *Param) GetString() (string, error) {
if p == nil { if p == nil {
return "", errMissingParameter return "", errMissingParameter
} }
str, ok := p.Value.(string) if p.IsNull() {
if !ok { return "", errNotAString
return "", errors.New("not a string")
} }
return str, nil var s string
err := json.Unmarshal(p.RawMessage, &s)
if err == nil {
return s, nil
}
var i int
err = json.Unmarshal(p.RawMessage, &i)
if err == nil {
return strconv.Itoa(i), nil
}
var b bool
err = json.Unmarshal(p.RawMessage, &b)
if err == nil {
if b {
return "true", nil
}
return "false", nil
}
return "", errNotAString
} }
// GetBoolean returns boolean value of the parameter. // GetBooleanStrict returns boolean value of the parameter.
func (p *Param) GetBoolean() bool { func (p *Param) GetBooleanStrict() (bool, error) {
if p == nil { if p == nil {
return false return false, errMissingParameter
} }
switch p.Type { if bytes.Equal(p.RawMessage, jsonTrueBytes) {
case NumberT: return true, nil
return p.Value != 0
case StringT:
return p.Value != ""
case BooleanT:
return p.Value == true
default:
return true
} }
if bytes.Equal(p.RawMessage, jsonFalseBytes) {
return false, nil
}
return false, errNotABool
} }
// GetInt returns int value of te parameter. // GetBoolean returns boolean value of the parameter or tries to cast parameter to a bool value.
func (p *Param) GetBoolean() (bool, error) {
if p == nil {
return false, errMissingParameter
}
if p.IsNull() {
return false, errNotABool
}
var b bool
err := json.Unmarshal(p.RawMessage, &b)
if err == nil {
return b, nil
}
var s string
err = json.Unmarshal(p.RawMessage, &s)
if err == nil {
return s != "", nil
}
var i int
err = json.Unmarshal(p.RawMessage, &i)
if err == nil {
return i != 0, nil
}
return false, errNotABool
}
// GetIntStrict returns int value of the parameter if the parameter is integer.
func (p *Param) GetIntStrict() (int, error) {
if p == nil {
return 0, errMissingParameter
}
if p.IsNull() {
return 0, errNotAnInt
}
var i int
err := json.Unmarshal(p.RawMessage, &i)
if err != nil {
return i, errNotAnInt
}
return i, nil
}
// GetInt returns int value of the parameter or tries to cast parameter to an int value.
func (p *Param) GetInt() (int, error) { func (p *Param) GetInt() (int, error) {
if p == nil { if p == nil {
return 0, errMissingParameter return 0, errMissingParameter
} }
i, ok := p.Value.(int) if p.IsNull() {
if ok { return 0, errNotAnInt
}
var i int
err := json.Unmarshal(p.RawMessage, &i)
if err == nil {
return i, nil return i, nil
} else if s, ok := p.Value.(string); ok { }
var s string
err = json.Unmarshal(p.RawMessage, &s)
if err == nil {
return strconv.Atoi(s) return strconv.Atoi(s)
} }
return 0, errors.New("not an integer") var b bool
err = json.Unmarshal(p.RawMessage, &b)
if err == nil {
i = 0
if b {
i = 1
}
return i, nil
}
return 0, errNotAnInt
} }
// GetArray returns a slice of Params stored in the parameter. // GetArray returns a slice of Params stored in the parameter.
@ -133,21 +214,14 @@ func (p *Param) GetArray() ([]Param, error) {
if p == nil { if p == nil {
return nil, errMissingParameter return nil, errMissingParameter
} }
a, ok := p.Value.([]Param) if p.IsNull() {
if ok { return nil, errNotAnArray
return a, nil
} }
raw, ok := p.Value.(json.RawMessage) a := []Param{}
if !ok { err := json.Unmarshal(p.RawMessage, &a)
return nil, errors.New("not an array")
}
a = []Param{}
err := json.Unmarshal(raw, &a)
if err != nil { if err != nil {
return nil, errors.New("not an array") return nil, errNotAnArray
} }
p.Value = a
return a, nil return a, nil
} }
@ -200,20 +274,9 @@ func (p *Param) GetFuncParam() (FuncParam, error) {
if p == nil { if p == nil {
return FuncParam{}, errMissingParameter return FuncParam{}, errMissingParameter
} }
fp, ok := p.Value.(FuncParam) fp := FuncParam{}
if ok { err := json.Unmarshal(p.RawMessage, &fp)
return fp, nil return fp, err
}
raw, ok := p.Value.(json.RawMessage)
if !ok {
return FuncParam{}, errors.New("not a function parameter")
}
err := json.Unmarshal(raw, &fp)
if err != nil {
return fp, err
}
p.Value = fp
return fp, nil
} }
// GetBytesHex returns []byte value of the parameter if // GetBytesHex returns []byte value of the parameter if
@ -240,25 +303,19 @@ func (p *Param) GetBytesBase64() ([]byte, error) {
// GetSignerWithWitness returns SignerWithWitness value of the parameter. // GetSignerWithWitness returns SignerWithWitness value of the parameter.
func (p *Param) GetSignerWithWitness() (SignerWithWitness, error) { func (p *Param) GetSignerWithWitness() (SignerWithWitness, error) {
c, ok := p.Value.(SignerWithWitness)
if ok {
return c, nil
}
raw, ok := p.Value.(json.RawMessage)
if !ok {
return SignerWithWitness{}, errors.New("not a signer")
}
aux := new(signerWithWitnessAux) aux := new(signerWithWitnessAux)
err := json.Unmarshal(raw, aux) err := json.Unmarshal(p.RawMessage, aux)
if err != nil { if err != nil {
return SignerWithWitness{}, errors.New("not a signer") return SignerWithWitness{}, fmt.Errorf("not a signer: %w", err)
} }
accParam := Param{StringT, aux.Account} acc, err := util.Uint160DecodeStringLE(strings.TrimPrefix(aux.Account, "0x"))
acc, err := accParam.GetUint160FromAddressOrHex()
if err != nil { if err != nil {
return SignerWithWitness{}, errors.New("not a signer") acc, err = address.StringToUint160(aux.Account)
} }
c = SignerWithWitness{ if err != nil {
return SignerWithWitness{}, fmt.Errorf("not a signer: %w", err)
}
c := SignerWithWitness{
Signer: transaction.Signer{ Signer: transaction.Signer{
Account: acc, Account: acc,
Scopes: aux.Scopes, Scopes: aux.Scopes,
@ -270,7 +327,6 @@ func (p *Param) GetSignerWithWitness() (SignerWithWitness, error) {
VerificationScript: aux.VerificationScript, VerificationScript: aux.VerificationScript,
}, },
} }
p.Value = c
return c, nil return c, nil
} }
@ -309,48 +365,9 @@ func (p Param) GetSignersWithWitnesses() ([]transaction.Signer, []transaction.Wi
return signers, witnesses, nil return signers, witnesses, nil
} }
// UnmarshalJSON implements json.Unmarshaler interface. // IsNull returns whether parameter represents JSON nil value.
func (p *Param) UnmarshalJSON(data []byte) error { func (p *Param) IsNull() bool {
r := bytes.NewReader(data) return bytes.Equal(p.RawMessage, jsonNullBytes)
jd := json.NewDecoder(r)
jd.UseNumber()
tok, err := jd.Token()
if err != nil {
return err
}
switch t := tok.(type) {
case json.Delim:
if t == json.Delim('[') {
var arr []Param
err := json.Unmarshal(data, &arr)
if err != nil {
return err
}
p.Type = ArrayT
p.Value = arr
} else {
p.Type = defaultT
p.Value = json.RawMessage(data)
}
case bool:
p.Type = BooleanT
p.Value = t
case float64: // unexpected because of `UseNumber`.
panic("unexpected")
case json.Number:
value, err := strconv.Atoi(string(t))
if err != nil {
return err
}
p.Type = NumberT
p.Value = value
case string:
p.Type = StringT
p.Value = t
default: // null
p.Type = defaultT
}
return nil
} }
// signerWithWitnessAux is an auxiluary struct for JSON marshalling. We need it because of // signerWithWitnessAux is an auxiluary struct for JSON marshalling. We need it because of

View file

@ -4,6 +4,7 @@ import (
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
@ -15,66 +16,178 @@ import (
) )
func TestParam_UnmarshalJSON(t *testing.T) { func TestParam_UnmarshalJSON(t *testing.T) {
msg := `["str1", 123, null, ["str2", 3], [{"type": "String", "value": "jajaja"}], type testCase struct {
{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"}, check func(t *testing.T, p *Param)
{"account": "NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag", "scopes": "Global"}, expectedRawMessage []byte
[{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": "Global"}]]` }
expected := Params{ msg := `["123", 123, null, ["str2", 3], [{"type": "String", "value": "jajaja"}],
{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"},
{"account": "NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag", "scopes": "Global"},
[{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": "Global"}]]`
expected := []testCase{
{ {
Type: StringT, check: func(t *testing.T, p *Param) {
Value: "str1", expectedS := "123"
}, actualS, err := p.GetStringStrict()
{ require.NoError(t, err)
Type: NumberT, require.Equal(t, expectedS, actualS)
Value: 123, actualS, err = p.GetString()
}, require.NoError(t, err)
{ require.Equal(t, expectedS, actualS)
Type: defaultT,
}, expectedI := 123
{ _, err = p.GetIntStrict()
Type: ArrayT, require.Error(t, err)
Value: []Param{ actualI, err := p.GetInt()
{ require.NoError(t, err)
Type: StringT, require.Equal(t, expectedI, actualI)
Value: "str2",
}, expectedB := true
{ _, err = p.GetBooleanStrict()
Type: NumberT, require.Error(t, err)
Value: 3, actualB, err := p.GetBoolean()
}, require.NoError(t, err)
require.Equal(t, expectedB, actualB)
_, err = p.GetArray()
require.Error(t, err)
}, },
expectedRawMessage: []byte(`"123"`),
}, },
{ {
Type: ArrayT, check: func(t *testing.T, p *Param) {
Value: []Param{ expectedS := "123"
{ _, err := p.GetStringStrict()
Type: defaultT, require.Error(t, err)
Value: json.RawMessage(`{"type": "String", "value": "jajaja"}`), actualS, err := p.GetString()
}, require.NoError(t, err)
require.Equal(t, expectedS, actualS)
expectedI := 123
actualI, err := p.GetIntStrict()
require.NoError(t, err)
require.Equal(t, expectedI, actualI)
actualI, err = p.GetInt()
require.NoError(t, err)
require.Equal(t, expectedI, actualI)
expectedB := true
_, err = p.GetBooleanStrict()
require.Error(t, err)
actualB, err := p.GetBoolean()
require.NoError(t, err)
require.Equal(t, expectedB, actualB)
_, err = p.GetArray()
require.Error(t, err)
}, },
expectedRawMessage: []byte(`123`),
}, },
{ {
Type: defaultT, check: func(t *testing.T, p *Param) {
Value: json.RawMessage(`{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"}`), _, err := p.GetStringStrict()
}, require.Error(t, err)
{ _, err = p.GetString()
Type: defaultT, require.Error(t, err)
Value: json.RawMessage(`{"account": "NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag", "scopes": "Global"}`),
}, _, err = p.GetIntStrict()
{ require.Error(t, err)
Type: ArrayT, _, err = p.GetInt()
Value: []Param{ require.Error(t, err)
{
Type: defaultT, _, err = p.GetBooleanStrict()
Value: json.RawMessage(`{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": "Global"}`), require.Error(t, err)
}, _, err = p.GetBoolean()
require.Error(t, err)
_, err = p.GetArray()
require.Error(t, err)
}, },
expectedRawMessage: []byte(`null`),
},
{
check: func(t *testing.T, p *Param) {
_, err := p.GetStringStrict()
require.Error(t, err)
_, err = p.GetString()
require.Error(t, err)
_, err = p.GetIntStrict()
require.Error(t, err)
_, err = p.GetInt()
require.Error(t, err)
_, err = p.GetBooleanStrict()
require.Error(t, err)
_, err = p.GetBoolean()
require.Error(t, err)
a, err := p.GetArray()
require.NoError(t, err)
require.Equal(t, []Param{
{RawMessage: json.RawMessage(`"str2"`)},
{RawMessage: json.RawMessage(`3`)},
}, a)
},
expectedRawMessage: []byte(`["str2", 3]`),
},
{
check: func(t *testing.T, p *Param) {
a, err := p.GetArray()
require.NoError(t, err)
require.Equal(t, 1, len(a))
fp, err := a[0].GetFuncParam()
require.NoError(t, err)
require.Equal(t, smartcontract.StringType, fp.Type)
strVal, err := fp.Value.GetStringStrict()
require.NoError(t, err)
require.Equal(t, "jajaja", strVal)
},
expectedRawMessage: []byte(`[{"type": "String", "value": "jajaja"}]`),
},
{
check: func(t *testing.T, p *Param) {
actual, err := p.GetSignerWithWitness()
require.NoError(t, err)
expectedAcc, err := util.Uint160DecodeStringLE("cadb3dc2faa3ef14a13b619c9a43124755aa2569")
require.NoError(t, err)
require.Equal(t, SignerWithWitness{Signer: transaction.Signer{Account: expectedAcc}}, actual)
},
expectedRawMessage: []byte(`{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"}`),
},
{
check: func(t *testing.T, p *Param) {
actual, err := p.GetSignerWithWitness()
require.NoError(t, err)
expectedAcc, err := address.StringToUint160("NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag")
require.NoError(t, err)
require.Equal(t, SignerWithWitness{Signer: transaction.Signer{Account: expectedAcc, Scopes: transaction.Global}}, actual)
},
expectedRawMessage: []byte(`{"account": "NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag", "scopes": "Global"}`),
},
{
check: func(t *testing.T, p *Param) {
actualSigs, actualWtns, err := p.GetSignersWithWitnesses()
require.NoError(t, err)
require.Equal(t, 1, len(actualSigs))
require.Equal(t, 1, len(actualWtns))
expectedAcc, err := util.Uint160DecodeStringLE("cadb3dc2faa3ef14a13b619c9a43124755aa2569")
require.NoError(t, err)
require.Equal(t, transaction.Signer{Account: expectedAcc, Scopes: transaction.Global}, actualSigs[0])
require.Equal(t, transaction.Witness{}, actualWtns[0])
},
expectedRawMessage: []byte(`[{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": "Global"}]`),
}, },
} }
var ps Params var ps Params
require.NoError(t, json.Unmarshal([]byte(msg), &ps)) require.NoError(t, json.Unmarshal([]byte(msg), &ps))
require.Equal(t, expected, ps) require.Equal(t, len(expected), len(ps))
for i, tc := range expected {
require.NotNil(t, ps[i])
require.Equal(t, json.RawMessage(tc.expectedRawMessage), ps[i].RawMessage, i)
tc.check(t, &ps[i])
}
} }
func TestGetWitness(t *testing.T) { func TestGetWitness(t *testing.T) {
@ -108,11 +221,10 @@ func TestGetWitness(t *testing.T) {
} }
for _, tc := range testCases { for _, tc := range testCases {
p := Param{Value: json.RawMessage(tc.raw)} p := Param{RawMessage: json.RawMessage(tc.raw)}
actual, err := p.GetSignerWithWitness() actual, err := p.GetSignerWithWitness()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, tc.expected, actual) require.Equal(t, tc.expected, actual)
require.Equal(t, tc.expected, p.Value)
actual, err = p.GetSignerWithWitness() // valid second invocation. actual, err = p.GetSignerWithWitness() // valid second invocation.
require.NoError(t, err) require.NoError(t, err)
@ -120,57 +232,24 @@ func TestGetWitness(t *testing.T) {
} }
} }
func TestParamGetString(t *testing.T) {
p := Param{StringT, "jajaja"}
str, err := p.GetString()
assert.Equal(t, "jajaja", str)
require.Nil(t, err)
p = Param{StringT, int(100500)}
_, err = p.GetString()
require.NotNil(t, err)
}
func TestParamGetInt(t *testing.T) {
p := Param{NumberT, int(100500)}
i, err := p.GetInt()
assert.Equal(t, 100500, i)
require.Nil(t, err)
p = Param{NumberT, "jajaja"}
_, err = p.GetInt()
require.NotNil(t, err)
}
func TestParamGetArray(t *testing.T) {
p := Param{ArrayT, []Param{{NumberT, 42}}}
a, err := p.GetArray()
assert.Equal(t, []Param{{NumberT, 42}}, a)
require.Nil(t, err)
p = Param{ArrayT, 42}
_, err = p.GetArray()
require.NotNil(t, err)
}
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{RawMessage: []byte(fmt.Sprintf(`"%s"`, 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, "0x" + gas} p = Param{RawMessage: []byte(fmt.Sprintf(`"0x%s"`, gas))}
u, err = p.GetUint256() u, err = p.GetUint256()
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, u256, u) assert.Equal(t, u256, u)
p = Param{StringT, 42} p = Param{RawMessage: []byte(`42`)}
_, err = p.GetUint256() _, err = p.GetUint256()
require.NotNil(t, err) require.NotNil(t, err)
p = Param{StringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"} p = Param{RawMessage: []byte(`"qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"`)}
_, err = p.GetUint256() _, err = p.GetUint256()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -178,16 +257,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{RawMessage: []byte(fmt.Sprintf(`"%s"`, 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{RawMessage: []byte(`42`)}
_, err = p.GetUint160FromHex() _, err = p.GetUint160FromHex()
require.NotNil(t, err) require.NotNil(t, err)
p = Param{StringT, "wwbefd26fdf6e4d957c11e078b24ebce6291456f"} p = Param{RawMessage: []byte(`"wwbefd26fdf6e4d957c11e078b24ebce6291456f"`)}
_, err = p.GetUint160FromHex() _, err = p.GetUint160FromHex()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -195,16 +274,16 @@ func TestParamGetUint160FromHex(t *testing.T) {
func TestParamGetUint160FromAddress(t *testing.T) { func TestParamGetUint160FromAddress(t *testing.T) {
in := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8" in := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8"
u160, _ := address.StringToUint160(in) u160, _ := address.StringToUint160(in)
p := Param{StringT, in} p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, 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{RawMessage: []byte(`42`)}
_, err = p.GetUint160FromAddress() _, err = p.GetUint160FromAddress()
require.NotNil(t, err) require.NotNil(t, err)
p = Param{StringT, "QK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"} p = Param{RawMessage: []byte(`"QK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"`)}
_, err = p.GetUint160FromAddress() _, err = p.GetUint160FromAddress()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -214,14 +293,14 @@ func TestParam_GetUint160FromAddressOrHex(t *testing.T) {
inHex, _ := address.StringToUint160(in) inHex, _ := address.StringToUint160(in)
t.Run("Address", func(t *testing.T) { t.Run("Address", func(t *testing.T) {
p := Param{StringT, in} p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))}
u, err := p.GetUint160FromAddressOrHex() u, err := p.GetUint160FromAddressOrHex()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, inHex, u) require.Equal(t, inHex, u)
}) })
t.Run("Hex", func(t *testing.T) { t.Run("Hex", func(t *testing.T) {
p := Param{StringT, inHex.StringLE()} p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, inHex.StringLE()))}
u, err := p.GetUint160FromAddressOrHex() u, err := p.GetUint160FromAddressOrHex()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, inHex, u) require.Equal(t, inHex, u)
@ -230,21 +309,15 @@ func TestParam_GetUint160FromAddressOrHex(t *testing.T) {
func TestParamGetFuncParam(t *testing.T) { func TestParamGetFuncParam(t *testing.T) {
fp := FuncParam{ fp := FuncParam{
Type: smartcontract.StringType, Type: smartcontract.StringType,
Value: Param{ Value: Param{RawMessage: []byte(`"jajaja"`)},
Type: StringT,
Value: "jajaja",
},
}
p := Param{
Type: FuncParamT,
Value: fp,
} }
p := Param{RawMessage: []byte(`{"type": "String", "value": "jajaja"}`)}
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{RawMessage: []byte(`42`)}
_, err = p.GetFuncParam() _, err = p.GetFuncParam()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -252,16 +325,17 @@ 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{RawMessage: []byte(fmt.Sprintf(`"%s"`, 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{RawMessage: []byte(`42`)}
_, err = p.GetBytesHex() h, err := p.GetBytesHex()
require.NotNil(t, err) assert.Equal(t, []byte{0x42}, h) // that's the way how C# works: 42 -> "42" -> []byte{0x42}
require.Nil(t, err)
p = Param{StringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"} p = Param{RawMessage: []byte(`"qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"`)}
_, err = p.GetBytesHex() _, err = p.GetBytesHex()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -270,16 +344,16 @@ func TestParamGetBytesBase64(t *testing.T) {
in := "Aj4A8DoW6HB84EXrQu6A05JFFUHuUQ3BjhyL77rFTXQm" in := "Aj4A8DoW6HB84EXrQu6A05JFFUHuUQ3BjhyL77rFTXQm"
inb, err := base64.StdEncoding.DecodeString(in) inb, err := base64.StdEncoding.DecodeString(in)
require.NoError(t, err) require.NoError(t, err)
p := Param{StringT, in} p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))}
bh, err := p.GetBytesBase64() bh, err := p.GetBytesBase64()
assert.Equal(t, inb, bh) assert.Equal(t, inb, bh)
require.Nil(t, err) require.Nil(t, err)
p = Param{StringT, 42} p = Param{RawMessage: []byte(`42`)}
_, err = p.GetBytesBase64() _, err = p.GetBytesBase64()
require.NotNil(t, err) require.NotNil(t, err)
p = Param{StringT, "@j4A8DoW6HB84EXrQu6A05JFFUHuUQ3BjhyL77rFTXQm"} p = Param{RawMessage: []byte("@j4A8DoW6HB84EXrQu6A05JFFUHuUQ3BjhyL77rFTXQm")}
_, err = p.GetBytesBase64() _, err = p.GetBytesBase64()
require.NotNil(t, err) require.NotNil(t, err)
} }
@ -296,12 +370,12 @@ func TestParamGetSigner(t *testing.T) {
VerificationScript: []byte{1, 2, 3}, VerificationScript: []byte{1, 2, 3},
}, },
} }
p := Param{Type: SignerWithWitnessT, Value: c} p := Param{RawMessage: []byte(`{"account": "0x0000000000000000000000000000000004030201", "scopes": "Global", "invocation": "AQID", "verification": "AQID"}`)}
actual, err := p.GetSignerWithWitness() actual, err := p.GetSignerWithWitness()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, c, actual) require.Equal(t, c, actual)
p = Param{Type: SignerWithWitnessT, Value: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}`} p = Param{RawMessage: []byte(`"not a signer"`)}
_, err = p.GetSignerWithWitness() _, err = p.GetSignerWithWitness()
require.Error(t, err) require.Error(t, err)
} }
@ -310,10 +384,7 @@ func TestParamGetSigners(t *testing.T) {
u1 := util.Uint160{1, 2, 3, 4} u1 := util.Uint160{1, 2, 3, 4}
u2 := util.Uint160{5, 6, 7, 8} u2 := util.Uint160{5, 6, 7, 8}
t.Run("from hashes", func(t *testing.T) { t.Run("from hashes", func(t *testing.T) {
p := Param{ArrayT, []Param{ p := Param{RawMessage: []byte(fmt.Sprintf(`["%s", "%s"]`, u1.StringLE(), u2.StringLE()))}
{Type: StringT, Value: u1.StringLE()},
{Type: StringT, Value: u2.StringLE()},
}}
actual, _, err := p.GetSignersWithWitnesses() actual, _, err := p.GetSignersWithWitnesses()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(actual)) require.Equal(t, 2, len(actual))
@ -321,56 +392,8 @@ func TestParamGetSigners(t *testing.T) {
require.True(t, u2.Equals(actual[1].Account)) require.True(t, u2.Equals(actual[1].Account))
}) })
t.Run("from signers", func(t *testing.T) {
c1 := SignerWithWitness{
Signer: transaction.Signer{
Account: u1,
Scopes: transaction.Global,
},
Witness: transaction.Witness{
InvocationScript: []byte{1, 2, 3},
VerificationScript: []byte{1, 2, 3},
},
}
c2 := SignerWithWitness{
Signer: transaction.Signer{
Account: u2,
Scopes: transaction.CustomContracts,
AllowedContracts: []util.Uint160{
{1, 2, 3},
{4, 5, 6},
},
},
}
p := Param{ArrayT, []Param{
{Type: SignerWithWitnessT, Value: c1},
{Type: SignerWithWitnessT, Value: c2},
}}
actualS, actualW, err := p.GetSignersWithWitnesses()
require.NoError(t, err)
require.Equal(t, 2, len(actualS))
require.Equal(t, transaction.Signer{
Account: c1.Account,
Scopes: c1.Scopes,
}, actualS[0])
require.Equal(t, transaction.Signer{
Account: c2.Account,
Scopes: c2.Scopes,
AllowedContracts: c2.AllowedContracts,
}, actualS[1])
require.EqualValues(t, 2, len(actualW))
require.EqualValues(t, transaction.Witness{
InvocationScript: c1.InvocationScript,
VerificationScript: c1.VerificationScript,
}, actualW[0])
require.Equal(t, transaction.Witness{}, actualW[1])
})
t.Run("bad format", func(t *testing.T) { t.Run("bad format", func(t *testing.T) {
p := Param{ArrayT, []Param{ p := Param{RawMessage: []byte(`"not a signer"`)}
{Type: StringT, Value: u1.StringLE()},
{Type: StringT, Value: "bla"},
}}
_, _, err := p.GetSignersWithWitnesses() _, _, err := p.GetSignersWithWitnesses()
require.Error(t, err) require.Error(t, err)
}) })

View file

@ -17,15 +17,6 @@ func (p Params) Value(index int) *Param {
return nil return nil
} }
// ValueWithType returns the param struct at the given index if it
// exists and matches the given type.
func (p Params) ValueWithType(index int, valType paramType) *Param {
if val := p.Value(index); val != nil && val.Type == valType {
return val
}
return nil
}
func (p Params) String() string { func (p Params) String() string {
return fmt.Sprintf("%v", []Param(p)) return fmt.Sprintf("%v", []Param(p))
} }

View file

@ -70,8 +70,8 @@ func ExpandArrayIntoScript(script *io.BinWriter, slice []Param) error {
} }
emit.Int(script, int64(val)) emit.Int(script, int64(val))
case smartcontract.BoolType: case smartcontract.BoolType:
val, ok := fp.Value.Value.(bool) val, err := fp.Value.GetBoolean() // not GetBooleanStrict(), because that's the way C# code works
if !ok { if err != nil {
return errors.New("not a bool") return errors.New("not a bool")
} }
if val { if val {
@ -102,29 +102,21 @@ func ExpandArrayIntoScript(script *io.BinWriter, slice []Param) error {
func CreateFunctionInvocationScript(contract util.Uint160, method string, params Params) ([]byte, error) { func CreateFunctionInvocationScript(contract util.Uint160, method string, params 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 { if slice, err := params[i].GetArray(); err == nil {
case StringT:
emit.String(script.BinWriter, params[i].String())
case NumberT:
num, err := params[i].GetInt()
if err != nil {
return nil, err
}
emit.String(script.BinWriter, strconv.Itoa(num))
case BooleanT:
val := params[i].GetBoolean()
emit.Bool(script.BinWriter, val)
case ArrayT:
slice, err := params[i].GetArray()
if err != nil {
return nil, err
}
err = ExpandArrayIntoScript(script.BinWriter, slice) err = ExpandArrayIntoScript(script.BinWriter, slice)
if err != nil { if err != nil {
return nil, err return nil, err
} }
emit.Int(script.BinWriter, int64(len(slice))) emit.Int(script.BinWriter, int64(len(slice)))
emit.Opcodes(script.BinWriter, opcode.PACK) emit.Opcodes(script.BinWriter, opcode.PACK)
} else if s, err := params[i].GetStringStrict(); err == nil {
emit.String(script.BinWriter, s)
} else if n, err := params[i].GetIntStrict(); err == nil {
emit.String(script.BinWriter, strconv.Itoa(n))
} else if b, err := params[i].GetBooleanStrict(); err == nil {
emit.Bool(script.BinWriter, b)
} else {
return nil, fmt.Errorf("failed to convert parmeter %s to script parameter", params[i])
} }
} }
if len(params) == 0 { if len(params) == 0 {

View file

@ -2,10 +2,10 @@ package request
import ( import (
"encoding/hex" "encoding/hex"
"fmt"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -13,7 +13,7 @@ import (
) )
func TestInvocationScriptCreationGood(t *testing.T) { func TestInvocationScriptCreationGood(t *testing.T) {
p := Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"} p := Param{RawMessage: []byte(`"50befd26fdf6e4d957c11e078b24ebce6291456f"`)}
contract, err := p.GetUint160FromHex() contract, err := p.GetUint160FromHex()
require.Nil(t, err) require.Nil(t, err)
@ -21,49 +21,60 @@ func TestInvocationScriptCreationGood(t *testing.T) {
ps Params ps Params
script string script string
}{{ }{{
ps: Params{{Type: StringT, Value: "transfer"}}, ps: Params{{RawMessage: []byte(`"transfer"`)}},
script: "c21f0c087472616e736665720c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "c21f0c087472616e736665720c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: NumberT, Value: 42}}, ps: Params{{RawMessage: []byte(`42`)}},
script: "c21f0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "c21f0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "m"}, {Type: BooleanT, Value: true}}, ps: Params{{RawMessage: []byte(`"m"`)}, {RawMessage: []byte(`true`)}},
script: "11db201f0c016d0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "11db201f0c016d0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{}}}, ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[]`)}},
script: "10c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "10c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.ByteArrayType, Value: Param{Type: StringT, Value: "AwEtR+diEK7HO+Oas9GG4KQP6Nhr+j1Pq/2le6E7iPlq"}}}}}}, ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "ByteString", "value": "AwEtR+diEK7HO+Oas9GG4KQP6Nhr+j1Pq/2le6E7iPlq"}]`)}},
script: "0c2103012d47e76210aec73be39ab3d186e0a40fe8d86bfa3d4fabfda57ba13b88f96a11c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "0c2103012d47e76210aec73be39ab3d186e0a40fe8d86bfa3d4fabfda57ba13b88f96a11c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.SignatureType, Value: Param{Type: StringT, Value: "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}}}}}}, ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "Signature", "value": "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}]`)}},
script: "0c404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f11c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "0c404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f11c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.StringType, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "String", "value": "50befd26fdf6e4d957c11e078b24ebce6291456f"}]`)}},
script: "0c283530626566643236666466366534643935376331316530373862323465626365363239313435366611c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "0c283530626566643236666466366534643935376331316530373862323465626365363239313435366611c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash160Type, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "Hash160", "value": "50befd26fdf6e4d957c11e078b24ebce6291456f"}]`)}},
script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5011c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5011c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash256Type, Value: Param{Type: StringT, Value: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}}}}}}, ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "Hash256", "value": "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}]`)}},
script: "0c20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6011c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "0c20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6011c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.PublicKeyType, Value: Param{Type: StringT, Value: "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}}}}}}, ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "PublicKey", "value": "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}]`)}},
script: "0c2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c111c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "0c2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c111c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.IntegerType, Value: Param{Type: NumberT, Value: 42}}}}}}, ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "Integer", "value": 42}]`)}},
script: "002a11c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "002a11c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: BooleanT, Value: true}}}}}}, ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "Integer", "value": "42"}]`)}}, // C# code doesn't use strict type assertions for JSON-ised params
script: "002a11c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, {
ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "Integer", "value": true}]`)}}, // C# code doesn't use strict type assertions for JSON-ised params
script: "1111c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "1111c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: BooleanT, Value: false}}}}}}, ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "Boolean", "value": true}]`)}},
script: "1111c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, {
ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "Boolean", "value": false}]`)}},
script: "1011c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "1011c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, {
ps: Params{{RawMessage: []byte(`"a"`)}, {RawMessage: []byte(`[{"type": "Boolean", "value": "blah"}]`)}}, // C# code doesn't use strict type assertions for JSON-ised params
script: "1111c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}} }}
for _, ps := range paramScripts { for i, ps := range paramScripts {
script, err := CreateFunctionInvocationScript(contract, ps.ps[0].String(), ps.ps[1:]) method, err := ps.ps[0].GetString()
require.NoError(t, err, fmt.Sprintf("testcase #%d", i))
script, err := CreateFunctionInvocationScript(contract, method, ps.ps[1:])
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, ps.script, hex.EncodeToString(script)) assert.Equal(t, ps.script, hex.EncodeToString(script), fmt.Sprintf("testcase #%d", i))
} }
} }
@ -71,25 +82,18 @@ func TestInvocationScriptCreationBad(t *testing.T) {
contract := util.Uint160{} contract := util.Uint160{}
var testParams = []Params{ var testParams = []Params{
{{Type: NumberT, Value: "qwerty"}}, {{RawMessage: []byte(`[{"type": "ByteArray", "value": "qwerty"}]`)}},
{{Type: ArrayT, Value: 42}}, {{RawMessage: []byte(`[{"type": "Signature", "value": "qwerty"}]`)}},
{{Type: ArrayT, Value: []Param{{Type: NumberT, Value: 42}}}}, {{RawMessage: []byte(`[{"type": "Hash160", "value": "qwerty"}]`)}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.ByteArrayType, Value: Param{Type: StringT, Value: "qwerty"}}}}}}, {{RawMessage: []byte(`[{"type": "Hash256", "value": "qwerty"}]`)}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.SignatureType, Value: Param{Type: StringT, Value: "qwerty"}}}}}}, {{RawMessage: []byte(`[{"type": "PublicKey", "value": 42}]`)}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.StringType, Value: Param{Type: NumberT, Value: 42}}}}}}, {{RawMessage: []byte(`[{"type": "PublicKey", "value": "qwerty"}]`)}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash160Type, Value: Param{Type: StringT, Value: "qwerty"}}}}}}, {{RawMessage: []byte(`[{"type": "Integer", "value": "123q"}]`)}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash256Type, Value: Param{Type: StringT, Value: "qwerty"}}}}}}, {{RawMessage: []byte(`[{"type": "Unknown"}]`)}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.PublicKeyType, Value: Param{Type: NumberT, Value: 42}}}}}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.PublicKeyType, Value: Param{Type: StringT, Value: "qwerty"}}}}}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.IntegerType, Value: Param{Type: StringT, Value: "qwerty"}}}}}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.IntegerType, Value: Param{Type: BooleanT, Value: true}}}}}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: NumberT, Value: 42}}}}}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "qwerty"}}}}}},
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.UnknownType, Value: Param{}}}}}},
} }
for _, ps := range testParams { for i, ps := range testParams {
_, err := CreateFunctionInvocationScript(contract, "", ps) _, err := CreateFunctionInvocationScript(contract, "", ps)
assert.NotNil(t, err) assert.NotNil(t, err, fmt.Sprintf("testcase #%d", i))
} }
} }
@ -99,11 +103,11 @@ func TestExpandArrayIntoScript(t *testing.T) {
Expected []byte Expected []byte
}{ }{
{ {
Input: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.StringType, Value: Param{Value: "a"}}}}, Input: []Param{{RawMessage: []byte(`{"type": "String", "value": "a"}`)}},
Expected: []byte{byte(opcode.PUSHDATA1), 1, byte('a')}, Expected: []byte{byte(opcode.PUSHDATA1), 1, byte('a')},
}, },
{ {
Input: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.ArrayType, Value: Param{Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.StringType, Value: Param{Value: "a"}}}}}}}}, Input: []Param{{RawMessage: []byte(`{"type": "Array", "value": [{"type": "String", "value": "a"}]}`)}},
Expected: []byte{byte(opcode.PUSHDATA1), 1, byte('a'), byte(opcode.PUSH1), byte(opcode.PACK)}, Expected: []byte{byte(opcode.PUSHDATA1), 1, byte('a'), byte(opcode.PUSH1), byte(opcode.PACK)},
}, },
} }
@ -115,10 +119,10 @@ func TestExpandArrayIntoScript(t *testing.T) {
} }
errorCases := [][]Param{ errorCases := [][]Param{
{ {
{Type: FuncParamT, Value: FuncParam{Type: smartcontract.ArrayType, Value: Param{Value: "a"}}}, {RawMessage: []byte(`{"type": "Array", "value": "a"}`)},
}, },
{ {
{Type: FuncParamT, Value: FuncParam{Type: smartcontract.ArrayType, Value: Param{Value: []Param{{Type: FuncParamT, Value: nil}}}}}, {RawMessage: []byte(`{"type": "Array", "value": null}`)},
}, },
} }
for _, c := range errorCases { for _, c := range errorCases {

View file

@ -53,7 +53,7 @@ type Request struct {
type In struct { type In struct {
JSONRPC string `json:"jsonrpc"` JSONRPC string `json:"jsonrpc"`
Method string `json:"method"` Method string `json:"method"`
RawParams json.RawMessage `json:"params,omitempty"` RawParams []Param `json:"params,omitempty"`
RawID json.RawMessage `json:"id,omitempty"` RawID json.RawMessage `json:"id,omitempty"`
} }
@ -134,16 +134,3 @@ func NewIn() *In {
JSONRPC: JSONRPCVersion, JSONRPC: JSONRPCVersion,
} }
} }
// 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, fmt.Errorf("error parsing params: %w", err)
}
return &params, nil
}

View file

@ -335,10 +335,7 @@ func (s *Server) handleIn(req *request.In, sub *subscriber) response.Abstract {
return s.packResponse(req, nil, response.NewInvalidParamsError("Problem parsing JSON", fmt.Errorf("invalid version, expected 2.0 got: '%s'", req.JSONRPC))) return s.packResponse(req, nil, response.NewInvalidParamsError("Problem parsing JSON", fmt.Errorf("invalid version, expected 2.0 got: '%s'", req.JSONRPC)))
} }
reqParams, err := req.Params() reqParams := request.Params(req.RawParams)
if err != nil {
return s.packResponse(req, nil, response.NewInvalidParamsError("Problem parsing request parameters", err))
}
s.log.Debug("processing rpc request", s.log.Debug("processing rpc request",
zap.String("method", req.Method), zap.String("method", req.Method),
@ -349,11 +346,11 @@ func (s *Server) handleIn(req *request.In, sub *subscriber) response.Abstract {
resErr = response.NewMethodNotFoundError(fmt.Sprintf("Method '%s' not supported", req.Method), nil) resErr = response.NewMethodNotFoundError(fmt.Sprintf("Method '%s' not supported", req.Method), nil)
handler, ok := rpcHandlers[req.Method] handler, ok := rpcHandlers[req.Method]
if ok { if ok {
res, resErr = handler(s, *reqParams) res, resErr = handler(s, reqParams)
} else if sub != nil { } else if sub != nil {
handler, ok := rpcWsHandlers[req.Method] handler, ok := rpcWsHandlers[req.Method]
if ok { if ok {
res, resErr = handler(s, *reqParams, sub) res, resErr = handler(s, reqParams, sub)
} }
} }
return s.packResponse(req, res, resErr) return s.packResponse(req, res, resErr)
@ -468,21 +465,18 @@ func (s *Server) blockHashFromParam(param *request.Param) (util.Uint256, *respon
return hash, response.ErrInvalidParams return hash, response.ErrInvalidParams
} }
switch param.Type { if _, err := param.GetStringStrict(); err == nil {
case request.StringT:
var err error var err error
hash, err = param.GetUint256() hash, err = param.GetUint256()
if err != nil { if err != nil {
return hash, response.ErrInvalidParams return hash, response.ErrInvalidParams
} }
case request.NumberT: } else {
num, err := s.blockHeightFromParam(param) num, err := s.blockHeightFromParam(param)
if err != nil { if err != nil {
return hash, response.ErrInvalidParams return hash, response.ErrInvalidParams
} }
hash = s.chain.GetHeaderHash(num) hash = s.chain.GetHeaderHash(num)
default:
return hash, response.ErrInvalidParams
} }
return hash, nil return hash, nil
} }
@ -499,7 +493,7 @@ func (s *Server) getBlock(reqParams request.Params) (interface{}, *response.Erro
return nil, response.NewInternalServerError(fmt.Sprintf("Problem locating block with hash: %s", hash), err) return nil, response.NewInternalServerError(fmt.Sprintf("Problem locating block with hash: %s", hash), err)
} }
if reqParams.Value(1).GetBoolean() { if v, _ := reqParams.Value(1).GetBoolean(); v {
return result.NewBlock(block, s.chain), nil return result.NewBlock(block, s.chain), nil
} }
writer := io.NewBufBinWriter() writer := io.NewBufBinWriter()
@ -508,11 +502,7 @@ func (s *Server) getBlock(reqParams request.Params) (interface{}, *response.Erro
} }
func (s *Server) getBlockHash(reqParams request.Params) (interface{}, *response.Error) { func (s *Server) getBlockHash(reqParams request.Params) (interface{}, *response.Error) {
param := reqParams.ValueWithType(0, request.NumberT) num, err := s.blockHeightFromParam(reqParams.Value(0))
if param == nil {
return nil, response.ErrInvalidParams
}
num, err := s.blockHeightFromParam(param)
if err != nil { if err != nil {
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
@ -557,7 +547,7 @@ func (s *Server) getPeers(_ request.Params) (interface{}, *response.Error) {
} }
func (s *Server) getRawMempool(reqParams request.Params) (interface{}, *response.Error) { func (s *Server) getRawMempool(reqParams request.Params) (interface{}, *response.Error) {
verbose := reqParams.Value(0).GetBoolean() verbose, _ := reqParams.Value(0).GetBoolean()
mp := s.chain.GetMemPool() mp := s.chain.GetMemPool()
hashList := make([]util.Uint256, 0) hashList := make([]util.Uint256, 0)
for _, item := range mp.GetVerifiedTransactions() { for _, item := range mp.GetVerifiedTransactions() {
@ -574,11 +564,15 @@ func (s *Server) getRawMempool(reqParams request.Params) (interface{}, *response
} }
func (s *Server) validateAddress(reqParams request.Params) (interface{}, *response.Error) { func (s *Server) validateAddress(reqParams request.Params) (interface{}, *response.Error) {
param := reqParams.Value(0) param, err := reqParams.Value(0).GetString()
if param == nil { if err != nil {
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
return validateAddress(param.Value), nil
return result.ValidateAddress{
Address: reqParams.Value(0),
IsValid: validateAddress(param),
}, nil
} }
// calculateNetworkFee calculates network fee for the transaction. // calculateNetworkFee calculates network fee for the transaction.
@ -669,11 +663,11 @@ func (s *Server) getApplicationLog(reqParams request.Params) (interface{}, *resp
trig := trigger.All trig := trigger.All
if len(reqParams) > 1 { if len(reqParams) > 1 {
trigString := reqParams.ValueWithType(1, request.StringT) trigString, err := reqParams.Value(1).GetString()
if trigString == nil { if err != nil {
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
trig, err = trigger.FromString(trigString.String()) trig, err = trigger.FromString(trigString)
if err != nil { if err != nil {
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
@ -902,19 +896,13 @@ func (s *Server) contractIDFromParam(param *request.Param) (int32, *response.Err
if param == nil { if param == nil {
return 0, response.ErrInvalidParams return 0, response.ErrInvalidParams
} }
switch param.Type { if scriptHash, err := param.GetUint160FromHex(); err == nil {
case request.StringT:
var err error
scriptHash, err := param.GetUint160FromHex()
if err != nil {
return 0, response.ErrInvalidParams
}
cs := s.chain.GetContractState(scriptHash) cs := s.chain.GetContractState(scriptHash)
if cs == nil { if cs == nil {
return 0, response.ErrUnknown return 0, response.ErrUnknown
} }
result = cs.ID result = cs.ID
case request.NumberT: } else {
id, err := param.GetInt() id, err := param.GetInt()
if err != nil { if err != nil {
return 0, response.ErrInvalidParams return 0, response.ErrInvalidParams
@ -923,8 +911,6 @@ func (s *Server) contractIDFromParam(param *request.Param) (int32, *response.Err
return 0, response.WrapErrorWithData(response.ErrInvalidParams, err) return 0, response.WrapErrorWithData(response.ErrInvalidParams, err)
} }
result = int32(id) result = int32(id)
default:
return 0, response.ErrInvalidParams
} }
return result, nil return result, nil
} }
@ -935,8 +921,7 @@ func (s *Server) contractScriptHashFromParam(param *request.Param) (util.Uint160
if param == nil { if param == nil {
return result, response.ErrInvalidParams return result, response.ErrInvalidParams
} }
switch param.Type { if _, err := param.GetStringStrict(); err == nil {
case request.StringT:
var err error var err error
result, err = param.GetUint160FromAddressOrHex() result, err = param.GetUint160FromAddressOrHex()
if err == nil { if err == nil {
@ -950,7 +935,7 @@ func (s *Server) contractScriptHashFromParam(param *request.Param) (util.Uint160
if err != nil { if err != nil {
return result, response.NewRPCError("Unknown contract: querying by name is supported for native contracts only", "", nil) return result, response.NewRPCError("Unknown contract: querying by name is supported for native contracts only", "", nil)
} }
case request.NumberT: } else {
id, err := param.GetInt() id, err := param.GetInt()
if err != nil { if err != nil {
return result, response.ErrInvalidParams return result, response.ErrInvalidParams
@ -962,8 +947,6 @@ func (s *Server) contractScriptHashFromParam(param *request.Param) (util.Uint160
if err != nil { if err != nil {
return result, response.NewRPCError("Unknown contract", "", err) return result, response.NewRPCError("Unknown contract", "", err)
} }
default:
return result, response.ErrInvalidParams
} }
return result, nil return result, nil
} }
@ -1244,7 +1227,7 @@ func (s *Server) getrawtransaction(reqParams request.Params) (interface{}, *resp
err = fmt.Errorf("invalid transaction %s: %w", txHash, err) err = fmt.Errorf("invalid transaction %s: %w", txHash, err)
return nil, response.NewRPCError("Unknown transaction", err.Error(), err) return nil, response.NewRPCError("Unknown transaction", err.Error(), err)
} }
if reqParams.Value(1).GetBoolean() { if v, _ := reqParams.Value(1).GetBoolean(); v {
if height == math.MaxUint32 { if height == math.MaxUint32 {
return result.NewTransactionOutputRaw(tx, nil, nil, s.chain), nil return result.NewTransactionOutputRaw(tx, nil, nil, s.chain), nil
} }
@ -1299,12 +1282,7 @@ func (s *Server) getNativeContracts(_ request.Params) (interface{}, *response.Er
// getBlockSysFee returns the system fees of the block, based on the specified index. // getBlockSysFee returns the system fees of the block, based on the specified index.
func (s *Server) getBlockSysFee(reqParams request.Params) (interface{}, *response.Error) { func (s *Server) getBlockSysFee(reqParams request.Params) (interface{}, *response.Error) {
param := reqParams.ValueWithType(0, request.NumberT) num, err := s.blockHeightFromParam(reqParams.Value(0))
if param == nil {
return 0, response.ErrInvalidParams
}
num, err := s.blockHeightFromParam(param)
if err != nil { if err != nil {
return 0, response.NewRPCError("Invalid height", "", nil) return 0, response.NewRPCError("Invalid height", "", nil)
} }
@ -1331,7 +1309,7 @@ func (s *Server) getBlockHeader(reqParams request.Params) (interface{}, *respons
return nil, respErr return nil, respErr
} }
verbose := reqParams.Value(1).GetBoolean() verbose, _ := reqParams.Value(1).GetBoolean()
h, err := s.chain.GetHeader(hash) h, err := s.chain.GetHeader(hash)
if err != nil { if err != nil {
return nil, response.NewRPCError("unknown block", "", nil) return nil, response.NewRPCError("unknown block", "", nil)
@ -1351,7 +1329,7 @@ func (s *Server) getBlockHeader(reqParams request.Params) (interface{}, *respons
// getUnclaimedGas returns unclaimed GAS amount of the specified address. // getUnclaimedGas returns unclaimed GAS amount of the specified address.
func (s *Server) getUnclaimedGas(ps request.Params) (interface{}, *response.Error) { func (s *Server) getUnclaimedGas(ps request.Params) (interface{}, *response.Error) {
u, err := ps.ValueWithType(0, request.StringT).GetUint160FromAddressOrHex() u, err := ps.Value(0).GetUint160FromAddressOrHex()
if err != nil { if err != nil {
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
@ -1423,7 +1401,11 @@ func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *respons
if len(tx.Signers) == 0 { if len(tx.Signers) == 0 {
tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.None}} tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.None}}
} }
script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1].String(), reqParams[2:checkWitnessHashesIndex]) method, err := reqParams[1].GetString()
if err != nil {
return nil, response.ErrInvalidParams
}
script, err := request.CreateFunctionInvocationScript(scriptHash, method, reqParams[2:checkWitnessHashesIndex])
if err != nil { if err != nil {
return nil, response.NewInternalServerError("can't create invocation script", err) return nil, response.NewInternalServerError("can't create invocation script", err)
} }
@ -1545,7 +1527,7 @@ func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash
// submitBlock broadcasts a raw block over the NEO network. // submitBlock broadcasts a raw block over the NEO network.
func (s *Server) submitBlock(reqParams request.Params) (interface{}, *response.Error) { func (s *Server) submitBlock(reqParams request.Params) (interface{}, *response.Error) {
blockBytes, err := reqParams.ValueWithType(0, request.StringT).GetBytesBase64() blockBytes, err := reqParams.Value(0).GetBytesBase64()
if err != nil { if err != nil {
return nil, response.NewInvalidParamsError("missing parameter or not base64", err) return nil, response.NewInvalidParamsError("missing parameter or not base64", err)
} }
@ -1670,34 +1652,27 @@ func (s *Server) subscribe(reqParams request.Params, sub *subscriber) (interface
// Optional filter. // Optional filter.
var filter interface{} var filter interface{}
if p := reqParams.Value(1); p != nil { if p := reqParams.Value(1); p != nil {
param, ok := p.Value.(json.RawMessage) param := *p
if !ok { jd := json.NewDecoder(bytes.NewReader(param.RawMessage))
return nil, response.ErrInvalidParams
}
jd := json.NewDecoder(bytes.NewReader(param))
jd.DisallowUnknownFields() jd.DisallowUnknownFields()
switch event { switch event {
case response.BlockEventID: case response.BlockEventID:
flt := new(request.BlockFilter) flt := new(request.BlockFilter)
err = jd.Decode(flt) err = jd.Decode(flt)
p.Type = request.BlockFilterT filter = *flt
p.Value = *flt
case response.TransactionEventID, response.NotaryRequestEventID: case response.TransactionEventID, response.NotaryRequestEventID:
flt := new(request.TxFilter) flt := new(request.TxFilter)
err = jd.Decode(flt) err = jd.Decode(flt)
p.Type = request.TxFilterT filter = *flt
p.Value = *flt
case response.NotificationEventID: case response.NotificationEventID:
flt := new(request.NotificationFilter) flt := new(request.NotificationFilter)
err = jd.Decode(flt) err = jd.Decode(flt)
p.Type = request.NotificationFilterT filter = *flt
p.Value = *flt
case response.ExecutionEventID: case response.ExecutionEventID:
flt := new(request.ExecutionFilter) flt := new(request.ExecutionFilter)
err = jd.Decode(flt) err = jd.Decode(flt)
if err == nil && (flt.State == "HALT" || flt.State == "FAULT") { if err == nil && (flt.State == "HALT" || flt.State == "FAULT") {
p.Type = request.ExecutionFilterT filter = *flt
p.Value = *flt
} else if err == nil { } else if err == nil {
err = errors.New("invalid state") err = errors.New("invalid state")
} }
@ -1705,7 +1680,6 @@ func (s *Server) subscribe(reqParams request.Params, sub *subscriber) (interface
if err != nil { if err != nil {
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
filter = p.Value
} }
s.subsLock.Lock() s.subsLock.Lock()
@ -1937,7 +1911,7 @@ drainloop:
func (s *Server) blockHeightFromParam(param *request.Param) (int, *response.Error) { func (s *Server) blockHeightFromParam(param *request.Param) (int, *response.Error) {
num, err := param.GetInt() num, err := param.GetInt()
if err != nil { if err != nil {
return 0, nil return 0, response.ErrInvalidParams
} }
if num < 0 || num > int(s.chain.BlockHeight()) { if num < 0 || num > int(s.chain.BlockHeight()) {
@ -1971,10 +1945,8 @@ func (s *Server) logRequestError(r *request.Request, jsonErr *response.Error) {
if r.In != nil { if r.In != nil {
logFields = append(logFields, zap.String("method", r.In.Method)) logFields = append(logFields, zap.String("method", r.In.Method))
params, err := r.In.Params() params := request.Params(r.In.RawParams)
if err == nil { logFields = append(logFields, zap.Any("params", params))
logFields = append(logFields, zap.Any("params", params))
}
} }
s.log.Error("Error encountered with rpc request", logFields...) s.log.Error("Error encountered with rpc request", logFields...)
@ -2021,11 +1993,10 @@ func (s *Server) writeHTTPServerResponse(r *request.Request, w http.ResponseWrit
// 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{}) bool {
resp := result.ValidateAddress{Address: addr}
if addr, ok := addr.(string); ok { if addr, ok := addr.(string); ok {
_, err := address.StringToUint160(addr) _, err := address.StringToUint160(addr)
resp.IsValid = (err == nil) return err == nil
} }
return resp return false
} }

View file

@ -455,10 +455,6 @@ var rpcTestCases = map[string][]rpcTestCase{
return &v return &v
}, },
}, },
{
params: "1",
fail: true,
},
}, },
"getblock": { "getblock": {
{ {