-
Notifications
You must be signed in to change notification settings - Fork 0
/
decode.go
129 lines (106 loc) · 3.17 KB
/
decode.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
package client
import (
"encoding/json"
"github.com/pkg/errors"
amino "github.com/tendermint/go-amino"
types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
)
func unmarshalResponseBytes(
cdc *amino.Codec,
responseBytes []byte,
expectedID types.JSONRPCIntID,
result interface{},
) (interface{}, error) {
// Read response. If rpc/core/types is imported, the result will unmarshal
// into the correct type.
response := &types.RPCResponse{}
if err := json.Unmarshal(responseBytes, response); err != nil {
return nil, errors.Wrap(err, "error unmarshalling")
}
if response.Error != nil {
return nil, response.Error
}
if err := validateAndVerifyID(response, expectedID); err != nil {
return nil, errors.Wrap(err, "wrong ID")
}
// Unmarshal the RawMessage into the result.
if err := cdc.UnmarshalJSON(response.Result, result); err != nil {
return nil, errors.Wrap(err, "error unmarshalling result")
}
return result, nil
}
func unmarshalResponseBytesArray(
cdc *amino.Codec,
responseBytes []byte,
expectedIDs []types.JSONRPCIntID,
results []interface{},
) ([]interface{}, error) {
var (
responses []types.RPCResponse
)
if err := json.Unmarshal(responseBytes, &responses); err != nil {
return nil, errors.Wrap(err, "error unmarshalling")
}
// No response error checking here as there may be a mixture of successful
// and unsuccessful responses.
if len(results) != len(responses) {
return nil, errors.Errorf(
"expected %d result objects into which to inject responses, but got %d",
len(responses),
len(results),
)
}
// Intersect IDs from responses with expectedIDs.
ids := make([]types.JSONRPCIntID, len(responses))
var ok bool
for i, resp := range responses {
ids[i], ok = resp.ID.(types.JSONRPCIntID)
if !ok {
return nil, errors.Errorf("expected JSONRPCIntID, got %T", resp.ID)
}
}
if err := validateResponseIDs(ids, expectedIDs); err != nil {
return nil, errors.Wrap(err, "wrong IDs")
}
for i := 0; i < len(responses); i++ {
if err := cdc.UnmarshalJSON(responses[i].Result, results[i]); err != nil {
return nil, errors.Wrapf(err, "error unmarshalling #%d result", i)
}
}
return results, nil
}
func validateResponseIDs(ids, expectedIDs []types.JSONRPCIntID) error {
m := make(map[types.JSONRPCIntID]bool, len(expectedIDs))
for _, expectedID := range expectedIDs {
m[expectedID] = true
}
for i, id := range ids {
if m[id] {
delete(m, id)
} else {
return errors.Errorf("unsolicited ID #%d: %v", i, id)
}
}
return nil
}
// From the JSON-RPC 2.0 spec:
// id: It MUST be the same as the value of the id member in the Request Object.
func validateAndVerifyID(res *types.RPCResponse, expectedID types.JSONRPCIntID) error {
if err := validateResponseID(res.ID); err != nil {
return err
}
if expectedID != res.ID.(types.JSONRPCIntID) { // validateResponseID ensured res.ID has the right type
return errors.Errorf("response ID (%d) does not match request ID (%d)", res.ID, expectedID)
}
return nil
}
func validateResponseID(id interface{}) error {
if id == nil {
return errors.New("no ID")
}
_, ok := id.(types.JSONRPCIntID)
if !ok {
return errors.Errorf("expected JSONRPCIntID, but got: %T", id)
}
return nil
}