github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/rpc/jsonrpc/client/decode.go (about)

     1  package client
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  
     8  	tmjson "github.com/tendermint/tendermint/libs/json"
     9  	types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
    10  )
    11  
    12  func unmarshalResponseBytes(
    13  	responseBytes []byte,
    14  	expectedID types.JSONRPCIntID,
    15  	result interface{},
    16  ) (interface{}, error) {
    17  
    18  	// Read response.  If rpc/core/types is imported, the result will unmarshal
    19  	// into the correct type.
    20  	response := &types.RPCResponse{}
    21  	if err := json.Unmarshal(responseBytes, response); err != nil {
    22  		return nil, fmt.Errorf("error unmarshalling: %w", err)
    23  	}
    24  
    25  	if response.Error != nil {
    26  		return nil, response.Error
    27  	}
    28  
    29  	if err := validateAndVerifyID(response, expectedID); err != nil {
    30  		return nil, fmt.Errorf("wrong ID: %w", err)
    31  	}
    32  
    33  	// Unmarshal the RawMessage into the result.
    34  	if err := tmjson.Unmarshal(response.Result, result); err != nil {
    35  		return nil, fmt.Errorf("error unmarshalling result: %w", err)
    36  	}
    37  
    38  	return result, nil
    39  }
    40  
    41  func unmarshalResponseBytesArray(
    42  	responseBytes []byte,
    43  	expectedIDs []types.JSONRPCIntID,
    44  	results []interface{},
    45  ) ([]interface{}, error) {
    46  
    47  	var (
    48  		responses []types.RPCResponse
    49  	)
    50  
    51  	if err := json.Unmarshal(responseBytes, &responses); err != nil {
    52  		return nil, fmt.Errorf("error unmarshalling: %w", err)
    53  	}
    54  
    55  	// No response error checking here as there may be a mixture of successful
    56  	// and unsuccessful responses.
    57  
    58  	if len(results) != len(responses) {
    59  		return nil, fmt.Errorf(
    60  			"expected %d result objects into which to inject responses, but got %d",
    61  			len(responses),
    62  			len(results),
    63  		)
    64  	}
    65  
    66  	// Intersect IDs from responses with expectedIDs.
    67  	ids := make([]types.JSONRPCIntID, len(responses))
    68  	var ok bool
    69  	for i, resp := range responses {
    70  		ids[i], ok = resp.ID.(types.JSONRPCIntID)
    71  		if !ok {
    72  			return nil, fmt.Errorf("expected JSONRPCIntID, got %T", resp.ID)
    73  		}
    74  	}
    75  	if err := validateResponseIDs(ids, expectedIDs); err != nil {
    76  		return nil, fmt.Errorf("wrong IDs: %w", err)
    77  	}
    78  
    79  	for i := 0; i < len(responses); i++ {
    80  		if err := tmjson.Unmarshal(responses[i].Result, results[i]); err != nil {
    81  			return nil, fmt.Errorf("error unmarshalling #%d result: %w", i, err)
    82  		}
    83  	}
    84  
    85  	return results, nil
    86  }
    87  
    88  func validateResponseIDs(ids, expectedIDs []types.JSONRPCIntID) error {
    89  	m := make(map[types.JSONRPCIntID]bool, len(expectedIDs))
    90  	for _, expectedID := range expectedIDs {
    91  		m[expectedID] = true
    92  	}
    93  
    94  	for i, id := range ids {
    95  		if m[id] {
    96  			delete(m, id)
    97  		} else {
    98  			return fmt.Errorf("unsolicited ID #%d: %v", i, id)
    99  		}
   100  	}
   101  
   102  	return nil
   103  }
   104  
   105  // From the JSON-RPC 2.0 spec:
   106  // id: It MUST be the same as the value of the id member in the Request Object.
   107  func validateAndVerifyID(res *types.RPCResponse, expectedID types.JSONRPCIntID) error {
   108  	if err := validateResponseID(res.ID); err != nil {
   109  		return err
   110  	}
   111  	if expectedID != res.ID.(types.JSONRPCIntID) { // validateResponseID ensured res.ID has the right type
   112  		return fmt.Errorf("response ID (%d) does not match request ID (%d)", res.ID, expectedID)
   113  	}
   114  	return nil
   115  }
   116  
   117  func validateResponseID(id interface{}) error {
   118  	if id == nil {
   119  		return errors.New("no ID")
   120  	}
   121  	_, ok := id.(types.JSONRPCIntID)
   122  	if !ok {
   123  		return fmt.Errorf("expected JSONRPCIntID, but got: %T", id)
   124  	}
   125  	return nil
   126  }