mirror of
https://github.com/tendermint/tendermint.git
synced 2026-02-06 03:50:46 +00:00
* Update Caller interface and its documentation. * Rework MapToRequest as ParamsToRequest. The old interface returned the result as well as populating it. Nothing was using this, so drop the duplicated value from the return signature. Clarify the documentation on the Caller type. Rework the MapToRequest helper to take an arbitrary value instead of only a map. This is groundwork for getting rid of the custom marshaling code. For now, however, the implementation preserves the existing behaviour for the map, until we can replace those.
121 lines
3.1 KiB
Go
121 lines
3.1 KiB
Go
package client
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
tmjson "github.com/tendermint/tendermint/libs/json"
|
|
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
|
)
|
|
|
|
func unmarshalResponseBytes(responseBytes []byte, expectedID rpctypes.JSONRPCIntID, result interface{}) error {
|
|
// Read response. If rpc/core/types is imported, the result will unmarshal
|
|
// into the correct type.
|
|
response := &rpctypes.RPCResponse{}
|
|
if err := json.Unmarshal(responseBytes, response); err != nil {
|
|
return fmt.Errorf("error unmarshaling: %w", err)
|
|
}
|
|
|
|
if response.Error != nil {
|
|
return response.Error
|
|
}
|
|
|
|
if err := validateAndVerifyID(response, expectedID); err != nil {
|
|
return fmt.Errorf("wrong ID: %w", err)
|
|
}
|
|
|
|
// Unmarshal the RawMessage into the result.
|
|
if err := tmjson.Unmarshal(response.Result, result); err != nil {
|
|
return fmt.Errorf("error unmarshaling result: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func unmarshalResponseBytesArray(
|
|
responseBytes []byte,
|
|
expectedIDs []rpctypes.JSONRPCIntID,
|
|
results []interface{},
|
|
) ([]interface{}, error) {
|
|
|
|
var (
|
|
responses []rpctypes.RPCResponse
|
|
)
|
|
|
|
if err := json.Unmarshal(responseBytes, &responses); err != nil {
|
|
return nil, fmt.Errorf("error unmarshaling: %w", err)
|
|
}
|
|
|
|
// No response error checking here as there may be a mixture of successful
|
|
// and unsuccessful responses.
|
|
|
|
if len(results) != len(responses) {
|
|
return nil, fmt.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([]rpctypes.JSONRPCIntID, len(responses))
|
|
var ok bool
|
|
for i, resp := range responses {
|
|
ids[i], ok = resp.ID.(rpctypes.JSONRPCIntID)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected JSONRPCIntID, got %T", resp.ID)
|
|
}
|
|
}
|
|
if err := validateResponseIDs(ids, expectedIDs); err != nil {
|
|
return nil, fmt.Errorf("wrong IDs: %w", err)
|
|
}
|
|
|
|
for i := 0; i < len(responses); i++ {
|
|
if err := tmjson.Unmarshal(responses[i].Result, results[i]); err != nil {
|
|
return nil, fmt.Errorf("error unmarshaling #%d result: %w", i, err)
|
|
}
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
func validateResponseIDs(ids, expectedIDs []rpctypes.JSONRPCIntID) error {
|
|
m := make(map[rpctypes.JSONRPCIntID]bool, len(expectedIDs))
|
|
for _, expectedID := range expectedIDs {
|
|
m[expectedID] = true
|
|
}
|
|
|
|
for i, id := range ids {
|
|
if m[id] {
|
|
delete(m, id)
|
|
} else {
|
|
return fmt.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 *rpctypes.RPCResponse, expectedID rpctypes.JSONRPCIntID) error {
|
|
if err := validateResponseID(res.ID); err != nil {
|
|
return err
|
|
}
|
|
if expectedID != res.ID.(rpctypes.JSONRPCIntID) { // validateResponseID ensured res.ID has the right type
|
|
return fmt.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.(rpctypes.JSONRPCIntID)
|
|
if !ok {
|
|
return fmt.Errorf("expected JSONRPCIntID, but got: %T", id)
|
|
}
|
|
return nil
|
|
}
|