github.com/dashpay/godash@v0.0.0-20160726055534-e038a21e0e3d/btcjson/jsonrpc.go (about)

     1  // Copyright (c) 2014 The btcsuite developers
     2  // Copyright (c) 2016 The Dash developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package btcjson
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  )
    12  
    13  // RPCErrorCode represents an error code to be used as a part of an RPCError
    14  // which is in turn used in a JSON-RPC Response object.
    15  //
    16  // A specific type is used to help ensure the wrong errors aren't used.
    17  type RPCErrorCode int
    18  
    19  // RPCError represents an error that is used as a part of a JSON-RPC Response
    20  // object.
    21  type RPCError struct {
    22  	Code    RPCErrorCode `json:"code,omitempty"`
    23  	Message string       `json:"message,omitempty"`
    24  }
    25  
    26  // Guarantee RPCError satisifies the builtin error interface.
    27  var _, _ error = RPCError{}, (*RPCError)(nil)
    28  
    29  // Error returns a string describing the RPC error.  This satisifies the
    30  // builtin error interface.
    31  func (e RPCError) Error() string {
    32  	return fmt.Sprintf("%d: %s", e.Code, e.Message)
    33  }
    34  
    35  // NewRPCError constructs and returns a new JSON-RPC error that is suitable
    36  // for use in a JSON-RPC Response object.
    37  func NewRPCError(code RPCErrorCode, message string) *RPCError {
    38  	return &RPCError{
    39  		Code:    code,
    40  		Message: message,
    41  	}
    42  }
    43  
    44  // IsValidIDType checks that the ID field (which can go in any of the JSON-RPC
    45  // requests, responses, or notifications) is valid.  JSON-RPC 1.0 allows any
    46  // valid JSON type.  JSON-RPC 2.0 (which bitcoind follows for some parts) only
    47  // allows string, number, or null, so this function restricts the allowed types
    48  // to that list.  This function is only provided in case the caller is manually
    49  // marshalling for some reason.    The functions which accept an ID in this
    50  // package already call this function to ensure the provided id is valid.
    51  func IsValidIDType(id interface{}) bool {
    52  	switch id.(type) {
    53  	case int, int8, int16, int32, int64,
    54  		uint, uint8, uint16, uint32, uint64,
    55  		float32, float64,
    56  		string,
    57  		nil:
    58  		return true
    59  	default:
    60  		return false
    61  	}
    62  }
    63  
    64  // Request is a type for raw JSON-RPC 1.0 requests.  The Method field identifies
    65  // the specific command type which in turns leads to different parameters.
    66  // Callers typically will not use this directly since this package provides a
    67  // statically typed command infrastructure which handles creation of these
    68  // requests, however this struct it being exported in case the caller wants to
    69  // construct raw requests for some reason.
    70  type Request struct {
    71  	Jsonrpc string            `json:"jsonrpc"`
    72  	Method  string            `json:"method"`
    73  	Params  []json.RawMessage `json:"params"`
    74  	ID      interface{}       `json:"id"`
    75  }
    76  
    77  // NewRequest returns a new JSON-RPC 1.0 request object given the provided id,
    78  // method, and parameters.  The parameters are marshalled into a json.RawMessage
    79  // for the Params field of the returned request object.  This function is only
    80  // provided in case the caller wants to construct raw requests for some reason.
    81  //
    82  // Typically callers will instead want to create a registered concrete command
    83  // type with the NewCmd or New<Foo>Cmd functions and call the MarshalCmd
    84  // function with that command to generate the marshalled JSON-RPC request.
    85  func NewRequest(id interface{}, method string, params []interface{}) (*Request, error) {
    86  	if !IsValidIDType(id) {
    87  		str := fmt.Sprintf("the id of type '%T' is invalid", id)
    88  		return nil, makeError(ErrInvalidType, str)
    89  	}
    90  
    91  	rawParams := make([]json.RawMessage, 0, len(params))
    92  	for _, param := range params {
    93  		marshalledParam, err := json.Marshal(param)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  		rawMessage := json.RawMessage(marshalledParam)
    98  		rawParams = append(rawParams, rawMessage)
    99  	}
   100  
   101  	return &Request{
   102  		Jsonrpc: "1.0",
   103  		ID:      id,
   104  		Method:  method,
   105  		Params:  rawParams,
   106  	}, nil
   107  }
   108  
   109  // Response is the general form of a JSON-RPC response.  The type of the Result
   110  // field varies from one command to the next, so it is implemented as an
   111  // interface.  The ID field has to be a pointer for Go to put a null in it when
   112  // empty.
   113  type Response struct {
   114  	Result json.RawMessage `json:"result"`
   115  	Error  *RPCError       `json:"error"`
   116  	ID     *interface{}    `json:"id"`
   117  }
   118  
   119  // NewResponse returns a new JSON-RPC response object given the provided id,
   120  // marshalled result, and RPC error.  This function is only provided in case the
   121  // caller wants to construct raw responses for some reason.
   122  //
   123  // Typically callers will instead want to create the fully marshalled JSON-RPC
   124  // response to send over the wire with the MarshalResponse function.
   125  func NewResponse(id interface{}, marshalledResult []byte, rpcErr *RPCError) (*Response, error) {
   126  	if !IsValidIDType(id) {
   127  		str := fmt.Sprintf("the id of type '%T' is invalid", id)
   128  		return nil, makeError(ErrInvalidType, str)
   129  	}
   130  
   131  	pid := &id
   132  	return &Response{
   133  		Result: marshalledResult,
   134  		Error:  rpcErr,
   135  		ID:     pid,
   136  	}, nil
   137  }
   138  
   139  // MarshalResponse marshals the passed id, result, and RPCError to a JSON-RPC
   140  // response byte slice that is suitable for transmission to a JSON-RPC client.
   141  func MarshalResponse(id interface{}, result interface{}, rpcErr *RPCError) ([]byte, error) {
   142  	marshalledResult, err := json.Marshal(result)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	response, err := NewResponse(id, marshalledResult, rpcErr)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	return json.Marshal(&response)
   151  }