forked from marcetin/parallelcoin
-
Notifications
You must be signed in to change notification settings - Fork 5
/
jsonrpc.go
131 lines (122 loc) · 5.01 KB
/
jsonrpc.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
package btcjson
import (
"encoding/json"
"fmt"
)
type (
// RPCError represents an error that is used as a part of a JSON-RPC Response object.
RPCError struct {
Code RPCErrorCode `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}
// RPCErrorCode represents an error code to be used as a part of an RPCError which is in turn used in a JSON-RPC
// Response object. A specific type is used to help ensure the wrong errors aren't used.
RPCErrorCode int
// Request is a type for raw JSON-RPC 1.0 requests. The Method field identifies the specific command type which in
// turns leads to different parameters. Callers typically will not use this directly since this package provides a
// statically typed command infrastructure which handles creation of these requests, however this struct it being
// exported in case the caller wants to construct raw requests for some reason.
Request struct {
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params []json.RawMessage `json:"netparams"`
ID interface{} `json:"id"`
}
// Response is the general form of a JSON-RPC response. The type of the Result field varies from one command to the
// next, so it is implemented as an interface. The ID field has to be a pointer for Go to put a null in it when
// empty.
Response struct {
Result json.RawMessage `json:"result"`
Error *RPCError `json:"error"`
ID *interface{} `json:"id"`
}
)
// Guarantee RPCError satisfies the builtin error interface.
var _, _ error = RPCError{}, (*RPCError)(nil)
// BTCJSONError returns a string describing the RPC error. This satisfies the builtin error interface.
func (e RPCError) Error() string {
return fmt.Sprintf("%d: %s", e.Code, e.Message)
}
// IsValidIDType checks that the ID field (which can go in any of the JSON-RPC requests, responses, or notifications) is
// valid. JSON-RPC 1.0 allows any valid JSON type. JSON-RPC 2.0 (which bitcoind follows for some parts) only allows
// string, number, or null, so this function restricts the allowed types to that list. This function is only provided in
// case the caller is manually marshalling for some reason. The functions which accept an ID in this package already
// call this function to ensure the provided id is valid.
func IsValidIDType(id interface{}) bool {
switch id.(type) {
case int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64,
float32, float64,
string,
nil:
return true
default:
return false
}
}
// MarshalResponse marshals the passed id, result, and RPCError to a JSON-RPC response byte slice that is suitable for
// transmission to a JSON-RPC client.
func MarshalResponse(id interface{}, result interface{}, rpcErr *RPCError) ([]byte, error) {
marshalledResult, e := json.Marshal(result)
if e != nil {
E.Ln(e)
return nil, e
}
response, e := NewResponse(id, marshalledResult, rpcErr)
if e != nil {
E.Ln(e)
return nil, e
}
return json.Marshal(&response)
}
// NewRPCError constructs and returns a new JSON-RPC error that is suitable for use in a JSON-RPC Response object.
func NewRPCError(code RPCErrorCode, message string) *RPCError {
return &RPCError{
Code: code,
Message: message,
}
}
// NewRequest returns a new JSON-RPC 1.0 request object given the provided id, method, and parameters. The parameters
// are marshalled into a json.RawMessage for the Params field of the returned request object. This function is only
// provided in case the caller wants to construct raw requests for some reason. Typically callers will instead want to
// create a registered concrete command type with the NewCmd or New<Foo>Cmd functions and call the MarshalCmd function
// with that command to generate the marshalled JSON-RPC request.
func NewRequest(id interface{}, method string, params []interface{}) (rq *Request, e error) {
if !IsValidIDType(id) {
str := fmt.Sprintf("the id of type '%T' is invalid", id)
return nil, makeError(ErrInvalidType, str)
}
rawParams := make([]json.RawMessage, 0, len(params))
for _, param := range params {
var marshalledParam []byte
marshalledParam, e = json.Marshal(param)
if e != nil {
E.Ln(e)
return nil, e
}
rawMessage := json.RawMessage(marshalledParam)
rawParams = append(rawParams, rawMessage)
}
return &Request{
Jsonrpc: "1.0",
ID: id,
Method: method,
Params: rawParams,
}, nil
}
// NewResponse returns a new JSON-RPC response object given the provided id, marshalled result, and RPC error. This
// function is only provided in case the caller wants to construct raw responses for some reason. Typically callers will
// instead want to create the fully marshalled JSON-RPC response to send over the wire with the MarshalResponse
// function.
func NewResponse(id interface{}, marshalledResult []byte, rpcErr *RPCError) (*Response, error) {
if !IsValidIDType(id) {
str := fmt.Sprintf("the id of type '%T' is invalid", id)
return nil, makeError(ErrInvalidType, str)
}
pid := &id
return &Response{
Result: marshalledResult,
Error: rpcErr,
ID: pid,
}, nil
}