decred.org/dcrdex@v1.0.5/client/asset/btc/electrum/jsonrpc.go (about) 1 // This code is available on the terms of the project LICENSE.md file, 2 // also available online at https://blueoakcouncil.org/license/1.0.0. 3 4 package electrum 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "reflect" 10 "strconv" 11 ) 12 13 type positional []any 14 15 type request struct { 16 Jsonrpc string `json:"jsonrpc"` 17 ID uint64 `json:"id"` 18 Method string `json:"method"` 19 Params json.RawMessage `json:"params"` // [] for positional args or {} for named args, no bare types 20 } 21 22 // RPCError represents a JSON-RPC error object. 23 type RPCError struct { 24 Code int `json:"code,omitempty"` 25 Message string `json:"message,omitempty"` 26 } 27 28 // Error satisfies the error interface. 29 func (e RPCError) Error() string { 30 return fmt.Sprintf("code %d: %q", e.Code, e.Message) 31 } 32 33 type response struct { 34 // The "jsonrpc" fields is ignored. 35 ID uint64 `json:"id"` // response to request 36 Method string `json:"method"` // notification for subscription 37 Result json.RawMessage `json:"result"` 38 Error *RPCError `json:"error"` 39 } 40 41 type ntfn = request // weird but true 42 type ntfnData struct { 43 Params json.RawMessage `json:"params"` 44 } 45 46 func prepareRequest(id uint64, method string, args any) ([]byte, error) { 47 // nil args should marshal as [] instead of null. 48 if args == nil { 49 args = []json.RawMessage{} 50 } 51 // else { 52 // switch reflect.TypeOf(args).Kind() { 53 // case reflect.Interface, reflect.Pointer, reflect.Slice, reflect.Map: 54 // if reflect.ValueOf(args).IsNil() { 55 // args = []json.RawMessage{} 56 // } 57 // default: 58 // } 59 // } 60 61 switch rt := reflect.TypeOf(args); rt.Kind() { 62 case reflect.Struct, reflect.Slice: 63 case reflect.Ptr: // allow pointer to struct 64 if rt.Elem().Kind() != reflect.Struct { 65 return nil, fmt.Errorf("invalid arg type %v, must be slice or struct", rt) 66 } 67 default: 68 return nil, fmt.Errorf("invalid arg type %v, must be slice or struct", rt) 69 } 70 71 params, err := json.Marshal(args) 72 if err != nil { 73 return nil, fmt.Errorf("failed to marshal arguments: %v", err) 74 } 75 req := &request{ 76 Jsonrpc: "2.0", // electrum wallet seems to respond with 2.0 regardless 77 ID: id, 78 Method: method, 79 Params: params, 80 } 81 return json.Marshal(req) 82 } 83 84 // floatString is for unmarshalling a string with a float like "123.34" directly 85 // into a float64 instead of a string and then converting later. 86 type floatString float64 87 88 func (fs *floatString) UnmarshalJSON(b []byte) error { 89 // Try to strip the string contents out of quotes. 90 var str string 91 if err := json.Unmarshal(b, &str); err != nil { 92 return err // wasn't a string 93 } 94 fl, err := strconv.ParseFloat(str, 64) 95 if err != nil { 96 return err // The string didn't contain a float. 97 } 98 *fs = floatString(fl) 99 return nil 100 }