mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-16 08:31:19 +00:00
110 lines
3.0 KiB
Go
110 lines
3.0 KiB
Go
package rpcclient
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
amino "github.com/tendermint/go-amino"
|
|
|
|
types "github.com/tendermint/tendermint/rpc/lib/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{}
|
|
err := json.Unmarshal(responseBytes, response)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error unmarshalling response")
|
|
}
|
|
|
|
if response.Error != nil {
|
|
return nil, errors.Wrap(response.Error, "response error")
|
|
}
|
|
|
|
if err = assertResponseIDEqual(response, expectedID); err != nil {
|
|
return nil, errors.Wrap(err, "error in response ID")
|
|
}
|
|
|
|
// Unmarshal the RawMessage into the result.
|
|
err = cdc.UnmarshalJSON(response.Result, result)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error unmarshalling rpc response result")
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func unmarshalResponseBytesArray(cdc *amino.Codec, responseBytes []byte,
|
|
expectedIDs []types.JSONRPCIntID, results []interface{}) ([]interface{}, error) {
|
|
|
|
var (
|
|
err error
|
|
responses []types.RPCResponse
|
|
)
|
|
err = json.Unmarshal(responseBytes, &responses)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error unmarshalling rpc response")
|
|
}
|
|
|
|
// 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))
|
|
}
|
|
|
|
for i, response := range responses {
|
|
if err := validateResponseID(response.ID); err != nil {
|
|
return nil, errors.Wrap(err, "invalid ID")
|
|
}
|
|
if !responseIDIsExpected(response.ID.(types.JSONRPCIntID), expectedIDs) {
|
|
return nil, errors.Errorf("unsolicited response ID: %v", response.ID)
|
|
}
|
|
if err = cdc.UnmarshalJSON(responses[i].Result, results[i]); err != nil {
|
|
return nil, errors.Wrap(err, "error unmarshalling response result")
|
|
}
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
func responseIDIsExpected(id types.JSONRPCIntID, expectedIDs []types.JSONRPCIntID) bool {
|
|
for _, expectedID := range expectedIDs {
|
|
if id == expectedID {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 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 assertResponseIDEqual(res *types.RPCResponse, expectedID types.JSONRPCIntID) error {
|
|
// URIClient does not have ID in response
|
|
if expectedID == -1 {
|
|
return nil
|
|
}
|
|
if err := validateResponseID(res.ID); err != nil {
|
|
return err
|
|
}
|
|
if expectedID != res.ID {
|
|
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")
|
|
}
|
|
id, ok := id.(types.JSONRPCIntID)
|
|
if !ok {
|
|
return errors.Errorf("expected int, but got: %T", id)
|
|
}
|
|
return nil
|
|
}
|