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 }