cmdparse.go raw

   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