mirror of
https://github.com/tendermint/tendermint.git
synced 2026-07-28 11:02:50 +00:00
Update the interface of the batch decoder to match the type signature of the single-response case. The caller provides the outputs, so there is no need to return them as well. No functional changes.
102 lines
2.8 KiB
Go
102 lines
2.8 KiB
Go
package client
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
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 := json.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{}) error {
|
|
var responses []rpctypes.RPCResponse
|
|
if err := json.Unmarshal(responseBytes, &responses); err != nil {
|
|
return fmt.Errorf("unmarshaling responses: %w", err)
|
|
} else if len(responses) != len(results) {
|
|
return fmt.Errorf("got %d results, wanted %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 fmt.Errorf("expected JSONRPCIntID, got %T", resp.ID)
|
|
}
|
|
}
|
|
if err := validateResponseIDs(ids, expectedIDs); err != nil {
|
|
return fmt.Errorf("wrong IDs: %w", err)
|
|
}
|
|
|
|
for i := 0; i < len(responses); i++ {
|
|
if err := json.Unmarshal(responses[i].Result, results[i]); err != nil {
|
|
return fmt.Errorf("error unmarshaling #%d result: %w", i, err)
|
|
}
|
|
}
|
|
return 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
|
|
}
|