1 package btcjson
2 3 import (
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "sort"
8 "strconv"
9 "strings"
10 )
11 12 // makeParams creates a slice of interface values for the given struct.
13 func makeParams(rt reflect.Type, rv reflect.Value) []interface{} {
14 numFields := rt.NumField()
15 params := make([]interface{}, 0, numFields)
16 for i := 0; i < numFields; i++ {
17 rtf := rt.Field(i)
18 rvf := rv.Field(i)
19 if rtf.Type.Kind() == reflect.Ptr {
20 if rvf.IsNil() {
21 break
22 }
23 rvf.Elem()
24 }
25 params = append(params, rvf.Interface())
26 }
27 return params
28 }
29 30 // MarshalCmd marshals the passed command to a JSON-RPC request byte slice that is suitable for transmission to an RPC
31 // server. The provided command type must be a registered type. All commands provided by this package are registered by
32 // default.
33 func MarshalCmd(id interface{}, cmd interface{}) ([]byte, error) {
34 // Look up the cmd type and error out if not registered.
35 rt := reflect.TypeOf(cmd)
36 registerLock.RLock()
37 method, ok := concreteTypeToMethod[rt]
38 registerLock.RUnlock()
39 if !ok {
40 str := fmt.Sprintf("%q is not registered", method)
41 return nil, makeError(ErrUnregisteredMethod, str)
42 }
43 // The provided command must not be nil.
44 rv := reflect.ValueOf(cmd)
45 if rv.IsNil() {
46 str := "the specified command is nil"
47 return nil, makeError(ErrInvalidType, str)
48 }
49 // Create a slice of interface values in the order of the struct fields while respecting pointer fields as optional
50 // netparams and only adding them if they are non-nil.
51 params := makeParams(rt.Elem(), rv.Elem())
52 // Generate and marshal the final JSON-RPC request.
53 rawCmd, e := NewRequest(id, method, params)
54 if e != nil {
55 E.Ln(e)
56 return nil, e
57 }
58 return json.Marshal(rawCmd)
59 }
60 61 // MarshalRequest marshals the passed command to a btcjson.Request
62 func MarshalRequest(id interface{}, cmd interface{}) (rawCmd *Request, e error) {
63 // Look up the cmd type and error out if not registered.
64 rt := reflect.TypeOf(cmd)
65 registerLock.RLock()
66 method, ok := concreteTypeToMethod[rt]
67 registerLock.RUnlock()
68 if !ok {
69 str := fmt.Sprintf("%q is not registered", method)
70 return nil, makeError(ErrUnregisteredMethod, str)
71 }
72 // The provided command must not be nil.
73 rv := reflect.ValueOf(cmd)
74 if rv.IsNil() {
75 str := "the specified command is nil"
76 return nil, makeError(ErrInvalidType, str)
77 }
78 // Create a slice of interface values in the order of the struct fields while respecting pointer fields as optional
79 // netparams and only adding them if they are non-nil.
80 params := makeParams(rt.Elem(), rv.Elem())
81 // Generate and marshal the final JSON-RPC request.
82 rawCmd, e = NewRequest(id, method, params)
83 if e != nil {
84 E.Ln(e)
85 return nil, e
86 }
87 return
88 }
89 90 // checkNumParams ensures the supplied number of netparams is at least the minimum required number for the command and
91 // less than the maximum allowed.
92 func checkNumParams(numParams int, info *MethodInfo) (e error) {
93 if numParams < info.NumReqParams || numParams > info.MaxParams {
94 if info.NumReqParams == info.MaxParams {
95 str := fmt.Sprintf(
96 "wrong number of netparams (expected "+
97 "%d, received %d)", info.NumReqParams,
98 numParams,
99 )
100 return makeError(ErrNumParams, str)
101 }
102 str := fmt.Sprintf(
103 "wrong number of netparams (expected "+
104 "between %d and %d, received %d)", info.NumReqParams,
105 info.MaxParams, numParams,
106 )
107 return makeError(ErrNumParams, str)
108 }
109 return nil
110 }
111 112 // populateDefaults populates default values into any remaining optional struct fields that did not have parameters
113 // explicitly provided. The caller should have previously checked that the number of parameters being passed is at least
114 // the required number of parameters to avoid unnecessary work in this function, but since required fields never have
115 // default values, it will work properly even without the check.
116 func populateDefaults(numParams int, info *MethodInfo, rv reflect.Value) {
117 // When there are no more parameters left in the supplied parameters, any remaining struct fields must be optional.
118 // Thus, populate them with their associated default value as needed.
119 for i := numParams; i < info.MaxParams; i++ {
120 rvf := rv.Field(i)
121 if defaultVal, ok := info.defaults[i]; ok {
122 rvf.Set(defaultVal)
123 }
124 }
125 }
126 127 // UnmarshalCmd unmarshalls a JSON-RPC request into a suitable concrete command so long as the method type contained within the marshalled request is registered.
128 func UnmarshalCmd(r *Request) (ii interface{}, e error) {
129 registerLock.RLock()
130 rtp, ok := methodToConcreteType[r.Method]
131 info := methodToInfo[r.Method]
132 registerLock.RUnlock()
133 if !ok {
134 str := fmt.Sprintf("%q is not registered", r.Method)
135 return nil, makeError(ErrUnregisteredMethod, str)
136 }
137 rt := rtp.Elem()
138 rvp := reflect.New(rt)
139 rv := rvp.Elem()
140 // Ensure the number of parameters are correct.
141 numParams := len(r.Params)
142 if e = checkNumParams(numParams, &info); E.Chk(e) {
143 return nil, e
144 }
145 // Loop through each of the struct fields and unmarshal the associated parameter into them.
146 for i := 0; i < numParams; i++ {
147 rvf := rv.Field(i)
148 // Unmarshal the parameter into the struct field.
149 concreteVal := rvf.Addr().Interface()
150 if e = json.Unmarshal(r.Params[i], &concreteVal); E.Chk(e) {
151 // The most common error is the wrong type, so explicitly detect that error and make it nicer.
152 fieldName := strings.ToLower(rt.Field(i).Name)
153 if jerr, ok := e.(*json.UnmarshalTypeError); ok {
154 str := fmt.Sprintf(
155 "parameter #%d '%s' must "+
156 "be type %v (got %v)", i+1, fieldName,
157 jerr.Type, jerr.Value,
158 )
159 return nil, makeError(ErrInvalidType, str)
160 }
161 // Fallback to showing the underlying error.
162 str := fmt.Sprintf(
163 "parameter #%d '%s' failed to "+
164 "unmarshal: %v", i+1, fieldName, e,
165 )
166 return nil, makeError(ErrInvalidType, str)
167 }
168 }
169 // When there are less supplied parameters than the total number of netparams, any remaining struct fields must be
170 // optional. Thus, populate them with their associated default value as needed.
171 if numParams < info.MaxParams {
172 populateDefaults(numParams, &info, rv)
173 }
174 return rvp.Interface(), nil
175 }
176 177 // isNumeric returns whether the passed reflect kind is a signed or unsigned integer of any magnitude or a float of any
178 // magnitude.
179 func isNumeric(kind reflect.Kind) bool {
180 switch kind {
181 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
182 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
183 reflect.Uint64, reflect.Float32, reflect.Float64:
184 return true
185 }
186 return false
187 }
188 189 // typesMaybeCompatible returns whether the source type can possibly be assigned to the destination type. This is
190 // intended as a relatively quick check to weed out obviously invalid conversions.
191 func typesMaybeCompatible(dest reflect.Type, src reflect.Type) bool {
192 // The same types are obviously compatible.
193 if dest == src {
194 return true
195 }
196 // When both types are numeric, they are potentially compatible.
197 srcKind := src.Kind()
198 destKind := dest.Kind()
199 if isNumeric(destKind) && isNumeric(srcKind) {
200 return true
201 }
202 if srcKind == reflect.String {
203 // Strings can potentially be converted to numeric types.
204 if isNumeric(destKind) {
205 return true
206 }
207 switch destKind {
208 // Strings can potentially be converted to bools by strconv.ParseBool.
209 case reflect.Bool:
210 return true
211 // Strings can be converted to any other type which has as underlying type of string.
212 case reflect.String:
213 return true
214 // Strings can potentially be converted to arrays, slice, structs, and maps via json.Unmarshal.
215 case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map:
216 return true
217 }
218 }
219 return false
220 }
221 222 // baseType returns the type of the argument after indirecting through all pointers along with how many indirections
223 // were necessary.
224 func baseType(arg reflect.Type) (reflect.Type, int) {
225 var numIndirects int
226 for arg.Kind() == reflect.Ptr {
227 arg = arg.Elem()
228 numIndirects++
229 }
230 return arg, numIndirects
231 }
232 233 // assignField is the main workhorse for the NewCmd function which handles assigning the provided source value to the
234 // destination field. It supports direct type assignments, indirection, conversion of numeric types, and unmarshaling of
235 // strings into arrays, slices, structs, and maps via json.Unmarshal.
236 func assignField(
237 paramNum int, fieldName string, dest reflect.Value,
238 src reflect.Value,
239 ) (e error) {
240 // Just error now when the types have no chance of being compatible.
241 destBaseType, destIndirects := baseType(dest.Type())
242 srcBaseType, srcIndirects := baseType(src.Type())
243 if !typesMaybeCompatible(destBaseType, srcBaseType) {
244 str := fmt.Sprintf(
245 "parameter #%d '%s' must be type %v (got "+
246 "%v)", paramNum, fieldName, destBaseType, srcBaseType,
247 )
248 return makeError(ErrInvalidType, str)
249 }
250 // Chk if it's possible to simply set the dest to the provided source. This is the case when the base types are
251 // the same or they are both pointers that can be indirected to be the same without needing to create pointers for
252 // the destination field.
253 if destBaseType == srcBaseType && srcIndirects >= destIndirects {
254 for i := 0; i < srcIndirects-destIndirects; i++ {
255 src = src.Elem()
256 }
257 dest.Set(src)
258 return nil
259 }
260 // When the destination has more indirects than the source, the extra pointers have to be created. Only create
261 // enough pointers to reach the same level of indirection as the source so the dest can simply be set to the
262 // provided source when the types are the same.
263 destIndirectsRemaining := destIndirects
264 if destIndirects > srcIndirects {
265 indirectDiff := destIndirects - srcIndirects
266 for i := 0; i < indirectDiff; i++ {
267 dest.Set(reflect.New(dest.Type().Elem()))
268 dest = dest.Elem()
269 destIndirectsRemaining--
270 }
271 }
272 if destBaseType == srcBaseType {
273 dest.Set(src)
274 return nil
275 }
276 // Make any remaining pointers needed to get to the base dest type since the above direct assign was not possible
277 // and conversions are done against the base types.
278 for i := 0; i < destIndirectsRemaining; i++ {
279 dest.Set(reflect.New(dest.Type().Elem()))
280 dest = dest.Elem()
281 }
282 // Indirect through to the base source value.
283 for src.Kind() == reflect.Ptr {
284 src = src.Elem()
285 }
286 // Perform supported type conversions.
287 switch src.Kind() {
288 // Source value is a signed integer of various magnitude.
289 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
290 reflect.Int64:
291 switch dest.Kind() {
292 // Destination is a signed integer of various magnitude.
293 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
294 reflect.Int64:
295 srcInt := src.Int()
296 if dest.OverflowInt(srcInt) {
297 str := fmt.Sprintf(
298 "parameter #%d '%s' "+
299 "overflows destination type %v",
300 paramNum, fieldName, destBaseType,
301 )
302 return makeError(ErrInvalidType, str)
303 }
304 dest.SetInt(srcInt)
305 // Destination is an unsigned integer of various magnitude.
306 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
307 reflect.Uint64:
308 srcInt := src.Int()
309 if srcInt < 0 || dest.OverflowUint(uint64(srcInt)) {
310 str := fmt.Sprintf(
311 "parameter #%d '%s' "+
312 "overflows destination type %v",
313 paramNum, fieldName, destBaseType,
314 )
315 return makeError(ErrInvalidType, str)
316 }
317 dest.SetUint(uint64(srcInt))
318 default:
319 str := fmt.Sprintf(
320 "parameter #%d '%s' must be type "+
321 "%v (got %v)", paramNum, fieldName, destBaseType,
322 srcBaseType,
323 )
324 return makeError(ErrInvalidType, str)
325 }
326 // Source value is an unsigned integer of various magnitude.
327 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
328 reflect.Uint64:
329 switch dest.Kind() {
330 // Destination is a signed integer of various magnitude.
331 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
332 reflect.Int64:
333 srcUint := src.Uint()
334 if srcUint > uint64(1<<63)-1 {
335 str := fmt.Sprintf(
336 "parameter #%d '%s' "+
337 "overflows destination type %v",
338 paramNum, fieldName, destBaseType,
339 )
340 return makeError(ErrInvalidType, str)
341 }
342 if dest.OverflowInt(int64(srcUint)) {
343 str := fmt.Sprintf(
344 "parameter #%d '%s' "+
345 "overflows destination type %v",
346 paramNum, fieldName, destBaseType,
347 )
348 return makeError(ErrInvalidType, str)
349 }
350 dest.SetInt(int64(srcUint))
351 // Destination is an unsigned integer of various magnitude.
352 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
353 reflect.Uint64:
354 srcUint := src.Uint()
355 if dest.OverflowUint(srcUint) {
356 str := fmt.Sprintf(
357 "parameter #%d '%s' "+
358 "overflows destination type %v",
359 paramNum, fieldName, destBaseType,
360 )
361 return makeError(ErrInvalidType, str)
362 }
363 dest.SetUint(srcUint)
364 default:
365 str := fmt.Sprintf(
366 "parameter #%d '%s' must be type "+
367 "%v (got %v)", paramNum, fieldName, destBaseType,
368 srcBaseType,
369 )
370 return makeError(ErrInvalidType, str)
371 }
372 // Source value is a float.
373 case reflect.Float32, reflect.Float64:
374 destKind := dest.Kind()
375 if destKind != reflect.Float32 && destKind != reflect.Float64 {
376 str := fmt.Sprintf(
377 "parameter #%d '%s' must be type "+
378 "%v (got %v)", paramNum, fieldName, destBaseType,
379 srcBaseType,
380 )
381 return makeError(ErrInvalidType, str)
382 }
383 srcFloat := src.Float()
384 if dest.OverflowFloat(srcFloat) {
385 str := fmt.Sprintf(
386 "parameter #%d '%s' overflows "+
387 "destination type %v", paramNum, fieldName,
388 destBaseType,
389 )
390 return makeError(ErrInvalidType, str)
391 }
392 dest.SetFloat(srcFloat)
393 // Source value is a string.
394 case reflect.String:
395 switch dest.Kind() {
396 // String -> bool
397 case reflect.Bool:
398 b, e := strconv.ParseBool(src.String())
399 if e != nil {
400 E.Ln(e)
401 str := fmt.Sprintf(
402 "parameter #%d '%s' must "+
403 "parse to a %v", paramNum, fieldName,
404 destBaseType,
405 )
406 return makeError(ErrInvalidType, str)
407 }
408 dest.SetBool(b)
409 // String -> signed integer of varying size.
410 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
411 reflect.Int64:
412 srcInt, e := strconv.ParseInt(src.String(), 0, 0)
413 if e != nil {
414 E.Ln(e)
415 str := fmt.Sprintf(
416 "parameter #%d '%s' must "+
417 "parse to a %v", paramNum, fieldName,
418 destBaseType,
419 )
420 return makeError(ErrInvalidType, str)
421 }
422 if dest.OverflowInt(srcInt) {
423 str := fmt.Sprintf(
424 "parameter #%d '%s' "+
425 "overflows destination type %v",
426 paramNum, fieldName, destBaseType,
427 )
428 return makeError(ErrInvalidType, str)
429 }
430 dest.SetInt(srcInt)
431 // String -> unsigned integer of varying size.
432 case reflect.Uint, reflect.Uint8, reflect.Uint16,
433 reflect.Uint32, reflect.Uint64:
434 srcUint, e := strconv.ParseUint(src.String(), 0, 0)
435 if e != nil {
436 E.Ln(e)
437 str := fmt.Sprintf(
438 "parameter #%d '%s' must "+
439 "parse to a %v", paramNum, fieldName,
440 destBaseType,
441 )
442 return makeError(ErrInvalidType, str)
443 }
444 if dest.OverflowUint(srcUint) {
445 str := fmt.Sprintf(
446 "parameter #%d '%s' "+
447 "overflows destination type %v",
448 paramNum, fieldName, destBaseType,
449 )
450 return makeError(ErrInvalidType, str)
451 }
452 dest.SetUint(srcUint)
453 // String -> float of varying size.
454 case reflect.Float32, reflect.Float64:
455 srcFloat, e := strconv.ParseFloat(src.String(), 0)
456 if e != nil {
457 E.Ln(e)
458 str := fmt.Sprintf(
459 "parameter #%d '%s' must "+
460 "parse to a %v", paramNum, fieldName,
461 destBaseType,
462 )
463 return makeError(ErrInvalidType, str)
464 }
465 if dest.OverflowFloat(srcFloat) {
466 str := fmt.Sprintf(
467 "parameter #%d '%s' "+
468 "overflows destination type %v",
469 paramNum, fieldName, destBaseType,
470 )
471 return makeError(ErrInvalidType, str)
472 }
473 dest.SetFloat(srcFloat)
474 // String -> string (typecast).
475 case reflect.String:
476 dest.SetString(src.String())
477 // String -> arrays, slices, structs, and maps via
478 // json.Unmarshal.
479 case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map:
480 concreteVal := dest.Addr().Interface()
481 e := json.Unmarshal([]byte(src.String()), &concreteVal)
482 if e != nil {
483 E.Ln(e)
484 str := fmt.Sprintf(
485 "parameter #%d '%s' must "+
486 "be valid JSON which unsmarshals to a %v",
487 paramNum, fieldName, destBaseType,
488 )
489 return makeError(ErrInvalidType, str)
490 }
491 dest.Set(reflect.ValueOf(concreteVal).Elem())
492 }
493 }
494 return nil
495 }
496 497 // NewCmd provides a generic mechanism to create a new command that can marshal to a JSON-RPC request while respecting
498 // the requirements of the provided method.
499 //
500 // The method must have been registered with the package already along with its type definition. All methods associated
501 // with the commands exported by this package are already registered by default.
502 //
503 // The arguments are most efficient when they are the exact same type as the underlying field in the command struct
504 // associated with the the method, however this function also will perform a variety of conversions to make it more
505 // flexible.
506 //
507 // This allows, for example, command line args which are strings to be passed unaltered. In particular, the following
508 // conversions are supported:
509 //
510 // - Conversion between any size signed or unsigned integer so long as the value does not overflow the destination
511 // type
512 //
513 // - Conversion between float32 and float64 so long as the value does not overflow the destination type
514 //
515 // - Conversion from string to boolean for everything strconv.ParseBool recognizes
516 //
517 // - Conversion from string to any size integer for everything strconv.ParseInt and strconv.ParseUint recognizes
518 //
519 // - Conversion from string to any size float for everything strconv.ParseFloat recognizes
520 //
521 // - Conversion from string to arrays, slices, structs, and maps by treating the string as marshalled JSON and calling
522 // json.Unmarshal into the destination field
523 func NewCmd(method string, args ...interface{}) (interface{}, error) {
524 // Look up details about the provided method. Any methods that aren't registered are an error.
525 registerLock.RLock()
526 rtp, ok := methodToConcreteType[method]
527 info := methodToInfo[method]
528 registerLock.RUnlock()
529 if !ok {
530 str := fmt.Sprintf("%q is not registered", method)
531 e := makeError(ErrUnregisteredMethod, str)
532 E.Chk(e)
533 return nil, e
534 }
535 // Ensure the number of parameters are correct.
536 numParams := len(args)
537 if e := checkNumParams(numParams, &info); E.Chk(e) {
538 return nil, e
539 }
540 // Create the appropriate command type for the method. Since all types are enforced to be a pointer to a struct at
541 // registration time, it's safe to indirect to the struct now.
542 rvp := reflect.New(rtp.Elem())
543 rv := rvp.Elem()
544 rt := rtp.Elem()
545 // Loop through each of the struct fields and assign the associated parameter into them after checking its type
546 // validity.
547 for i := 0; i < numParams; i++ {
548 // Attempt to assign each of the arguments to the according struct field.
549 rvf := rv.Field(i)
550 fieldName := strings.ToLower(rt.Field(i).Name)
551 e := assignField(i+1, fieldName, rvf, reflect.ValueOf(args[i]))
552 if e != nil {
553 E.Ln(e)
554 return nil, e
555 }
556 }
557 return rvp.Interface(), nil
558 }
559 560 // MethodToInfo gets the information about a method
561 func MethodToInfo(method string) *MethodInfo {
562 F.Ln(method)
563 T.S(methodToInfo[method])
564 if v, ok := methodToInfo[method]; ok {
565 return &v
566 }
567 return nil
568 }
569 570 // GetMethods returns the list of methods available
571 func GetMethods() (out []string) {
572 out = make([]string, len(methodToInfo))
573 var i int
574 for v := range methodToInfo {
575 out[i] = v
576 i++
577 }
578 sort.Strings(out)
579 return
580 }
581