forked from tendermint/tendermint
-
Notifications
You must be signed in to change notification settings - Fork 2
/
types.go
187 lines (157 loc) · 5.02 KB
/
types.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package rpctypes
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/pkg/errors"
amino "github.com/tendermint/go-amino"
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
)
//----------------------------------------
// REQUEST
type RPCRequest struct {
JSONRPC string `json:"jsonrpc"`
ID string `json:"id"`
Method string `json:"method"`
Params json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{}
}
func NewRPCRequest(id string, method string, params json.RawMessage) RPCRequest {
return RPCRequest{
JSONRPC: "2.0",
ID: id,
Method: method,
Params: params,
}
}
func (req RPCRequest) String() string {
return fmt.Sprintf("[%s %s]", req.ID, req.Method)
}
func MapToRequest(cdc *amino.Codec, id string, method string, params map[string]interface{}) (RPCRequest, error) {
var params_ = make(map[string]json.RawMessage, len(params))
for name, value := range params {
valueJSON, err := cdc.MarshalJSON(value)
if err != nil {
return RPCRequest{}, err
}
params_[name] = valueJSON
}
payload, err := json.Marshal(params_) // NOTE: Amino doesn't handle maps yet.
if err != nil {
return RPCRequest{}, err
}
request := NewRPCRequest(id, method, payload)
return request, nil
}
func ArrayToRequest(cdc *amino.Codec, id string, method string, params []interface{}) (RPCRequest, error) {
var params_ = make([]json.RawMessage, len(params))
for i, value := range params {
valueJSON, err := cdc.MarshalJSON(value)
if err != nil {
return RPCRequest{}, err
}
params_[i] = valueJSON
}
payload, err := json.Marshal(params_) // NOTE: Amino doesn't handle maps yet.
if err != nil {
return RPCRequest{}, err
}
request := NewRPCRequest(id, method, payload)
return request, nil
}
//----------------------------------------
// RESPONSE
type RPCError struct {
Code int `json:"code"`
Message string `json:"message"`
Data string `json:"data,omitempty"`
}
func (err RPCError) Error() string {
const baseFormat = "RPC error %v - %s"
if err.Data != "" {
return fmt.Sprintf(baseFormat+": %s", err.Code, err.Message, err.Data)
}
return fmt.Sprintf(baseFormat, err.Code, err.Message)
}
type RPCResponse struct {
JSONRPC string `json:"jsonrpc"`
ID string `json:"id"`
Result json.RawMessage `json:"result,omitempty"`
Error *RPCError `json:"error,omitempty"`
}
func NewRPCSuccessResponse(cdc *amino.Codec, id string, res interface{}) RPCResponse {
var rawMsg json.RawMessage
if res != nil {
var js []byte
js, err := cdc.MarshalJSON(res)
if err != nil {
return RPCInternalError(id, errors.Wrap(err, "Error marshalling response"))
}
rawMsg = json.RawMessage(js)
}
return RPCResponse{JSONRPC: "2.0", ID: id, Result: rawMsg}
}
func NewRPCErrorResponse(id string, code int, msg string, data string) RPCResponse {
return RPCResponse{
JSONRPC: "2.0",
ID: id,
Error: &RPCError{Code: code, Message: msg, Data: data},
}
}
func (resp RPCResponse) String() string {
if resp.Error == nil {
return fmt.Sprintf("[%s %v]", resp.ID, resp.Result)
}
return fmt.Sprintf("[%s %s]", resp.ID, resp.Error)
}
func RPCParseError(id string, err error) RPCResponse {
return NewRPCErrorResponse(id, -32700, "Parse error. Invalid JSON", err.Error())
}
func RPCInvalidRequestError(id string, err error) RPCResponse {
return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error())
}
func RPCMethodNotFoundError(id string) RPCResponse {
return NewRPCErrorResponse(id, -32601, "Method not found", "")
}
func RPCInvalidParamsError(id string, err error) RPCResponse {
return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error())
}
func RPCInternalError(id string, err error) RPCResponse {
return NewRPCErrorResponse(id, -32603, "Internal error", err.Error())
}
func RPCServerError(id string, err error) RPCResponse {
return NewRPCErrorResponse(id, -32000, "Server error", err.Error())
}
//----------------------------------------
// *wsConnection implements this interface.
type WSRPCConnection interface {
GetRemoteAddr() string
WriteRPCResponse(resp RPCResponse)
TryWriteRPCResponse(resp RPCResponse) bool
GetEventSubscriber() EventSubscriber
Codec() *amino.Codec
}
// EventSubscriber mirros tendermint/tendermint/types.EventBusSubscriber
type EventSubscriber interface {
Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, out chan<- interface{}) error
Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error
UnsubscribeAll(ctx context.Context, subscriber string) error
}
// websocket-only RPCFuncs take this as the first parameter.
type WSRPCContext struct {
Request RPCRequest
WSRPCConnection
}
//----------------------------------------
// SOCKETS
//
// Determine if its a unix or tcp socket.
// If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port
// TODO: deprecate
func SocketType(listenAddr string) string {
socketType := "unix"
if len(strings.Split(listenAddr, ":")) >= 2 {
socketType = "tcp"
}
return socketType
}