diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 1c25d9e75..51eaf1401 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -2,7 +2,6 @@ package rpcserver import ( "bytes" - "encoding/base64" "encoding/hex" "encoding/json" "fmt" @@ -16,7 +15,6 @@ import ( "github.com/gorilla/websocket" "github.com/pkg/errors" //wire "github.com/tendermint/go-wire" - "github.com/tendermint/go-wire/data" types "github.com/tendermint/tendermint/rpc/lib/types" cmn "github.com/tendermint/tmlibs/common" events "github.com/tendermint/tmlibs/events" @@ -206,32 +204,7 @@ func jsonParamsToArgsWS(rpcFunc *RPCFunc, paramsI interface{}, wsCtx types.WSRPC func _jsonObjectToArg(ty reflect.Type, object interface{}) (reflect.Value, error) { var err error v := reflect.New(ty) - - // if the object is a byte array, we need to decode it - if ty.Kind() == reflect.Slice && ty.Elem().Kind() == reflect.Uint8 { - s, ok := object.(string) - if !ok { - return v, fmt.Errorf("cmah") - } - - // if its data.Bytes, use hex - // else use base64 - dbty := reflect.TypeOf(data.Bytes{}) - if ty == dbty { - decoded, err := hex.DecodeString(s) - if err != nil { - return v, err - } - object = decoded - } else { - decoded, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return v, err - } - object = decoded - } - } - v.Elem().Set(reflect.ValueOf(object)) + readJSONObjectPtr(v.Interface(), object, &err) if err != nil { return v, err } diff --git a/rpc/lib/server/wire.go b/rpc/lib/server/wire.go new file mode 100644 index 000000000..ad3d61f8c --- /dev/null +++ b/rpc/lib/server/wire.go @@ -0,0 +1,290 @@ +package rpcserver + +import ( + "encoding/base64" + "encoding/hex" + "reflect" + "time" + + "github.com/pkg/errors" + + "github.com/tendermint/go-wire" + "github.com/tendermint/go-wire/data" + cmn "github.com/tendermint/tmlibs/common" +) + +var ( + timeType = wire.GetTypeFromStructDeclaration(struct{ time.Time }{}) +) + +func readJSONObjectPtr(o interface{}, object interface{}, err *error) interface{} { + rv, rt := reflect.ValueOf(o), reflect.TypeOf(o) + if rv.Kind() == reflect.Ptr { + readReflectJSON(rv.Elem(), rt.Elem(), wire.Options{}, object, err) + } else { + cmn.PanicSanity("ReadJSON(Object)Ptr expects o to be a pointer") + } + return o +} + +func readByteJSON(o interface{}) (typeByte byte, rest interface{}, err error) { + oSlice, ok := o.([]interface{}) + if !ok { + err = errors.New(cmn.Fmt("Expected type [Byte,?] but got type %v", reflect.TypeOf(o))) + return + } + if len(oSlice) != 2 { + err = errors.New(cmn.Fmt("Expected [Byte,?] len 2 but got len %v", len(oSlice))) + return + } + typeByte_, ok := oSlice[0].(float64) + typeByte = byte(typeByte_) + rest = oSlice[1] + return +} + +// Contract: Caller must ensure that rt is supported +// (e.g. is recursively composed of supported native types, and structs and slices.) +// rv and rt refer to the object we're unmarhsaling into, whereas o is the result of naiive json unmarshal (map[string]interface{}) +func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o interface{}, err *error) { + + // Get typeInfo + typeInfo := wire.GetTypeInfo(rt) + + if rt.Kind() == reflect.Interface { + if !typeInfo.IsRegisteredInterface { + // There's no way we can read such a thing. + *err = errors.New(cmn.Fmt("Cannot read unregistered interface type %v", rt)) + return + } + if o == nil { + return // nil + } + typeByte, rest, err_ := readByteJSON(o) + if err_ != nil { + *err = err_ + return + } + crt, ok := typeInfo.ByteToType[typeByte] + if !ok { + *err = errors.New(cmn.Fmt("Byte %X not registered for interface %v", typeByte, rt)) + return + } + if crt.Kind() == reflect.Ptr { + crt = crt.Elem() + crv := reflect.New(crt) + readReflectJSON(crv.Elem(), crt, opts, rest, err) + rv.Set(crv) // NOTE: orig rv is ignored. + } else { + crv := reflect.New(crt).Elem() + readReflectJSON(crv, crt, opts, rest, err) + rv.Set(crv) // NOTE: orig rv is ignored. + } + return + } + + if rt.Kind() == reflect.Ptr { + if o == nil { + return // nil + } + // Create new struct if rv is nil. + if rv.IsNil() { + newRv := reflect.New(rt.Elem()) + rv.Set(newRv) + rv = newRv + } + // Dereference pointer + rv, rt = rv.Elem(), rt.Elem() + typeInfo = wire.GetTypeInfo(rt) + // continue... + } + + switch rt.Kind() { + case reflect.Array: + elemRt := rt.Elem() + length := rt.Len() + if elemRt.Kind() == reflect.Uint8 { + // Special case: Bytearrays + oString, ok := o.(string) + if !ok { + *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) + return + } + + // if its data.Bytes, use hex; else use base64 + dbty := reflect.TypeOf(data.Bytes{}) + var buf []byte + var err_ error + if rt == dbty { + buf, err_ = hex.DecodeString(oString) + } else { + buf, err_ = base64.StdEncoding.DecodeString(oString) + } + if err_ != nil { + *err = err_ + return + } + if len(buf) != length { + *err = errors.New(cmn.Fmt("Expected bytearray of length %v but got %v", length, len(buf))) + return + } + //log.Info("Read bytearray", "bytes", buf) + reflect.Copy(rv, reflect.ValueOf(buf)) + } else { + oSlice, ok := o.([]interface{}) + if !ok { + *err = errors.New(cmn.Fmt("Expected array of %v but got type %v", rt, reflect.TypeOf(o))) + return + } + if len(oSlice) != length { + *err = errors.New(cmn.Fmt("Expected array of length %v but got %v", length, len(oSlice))) + return + } + for i := 0; i < length; i++ { + elemRv := rv.Index(i) + readReflectJSON(elemRv, elemRt, opts, oSlice[i], err) + } + //log.Info("Read x-array", "x", elemRt, "length", length) + } + + case reflect.Slice: + elemRt := rt.Elem() + if elemRt.Kind() == reflect.Uint8 { + // Special case: Byteslices + oString, ok := o.(string) + if !ok { + *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) + return + } + // if its data.Bytes, use hex; else use base64 + dbty := reflect.TypeOf(data.Bytes{}) + var buf []byte + var err_ error + if rt == dbty { + buf, err_ = hex.DecodeString(oString) + } else { + buf, err_ = base64.StdEncoding.DecodeString(oString) + } + if err_ != nil { + *err = err_ + return + } + //log.Info("Read byteslice", "bytes", byteslice) + rv.Set(reflect.ValueOf(buf)) + } else { + // Read length + oSlice, ok := o.([]interface{}) + if !ok { + *err = errors.New(cmn.Fmt("Expected array of %v but got type %v", rt, reflect.TypeOf(o))) + return + } + length := len(oSlice) + //log.Info("Read slice", "length", length) + sliceRv := reflect.MakeSlice(rt, length, length) + // Read elems + for i := 0; i < length; i++ { + elemRv := sliceRv.Index(i) + readReflectJSON(elemRv, elemRt, opts, oSlice[i], err) + } + rv.Set(sliceRv) + } + + case reflect.Struct: + if rt == timeType { + // Special case: time.Time + str, ok := o.(string) + if !ok { + *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) + return + } + // try three ways, seconds, milliseconds, or microseconds... + t, err_ := time.Parse(time.RFC3339Nano, str) + if err_ != nil { + *err = err_ + return + } + rv.Set(reflect.ValueOf(t)) + } else { + if typeInfo.Unwrap { + f := typeInfo.Fields[0] + fieldIdx, fieldType, opts := f.Index, f.Type, f.Options + fieldRv := rv.Field(fieldIdx) + readReflectJSON(fieldRv, fieldType, opts, o, err) + } else { + oMap, ok := o.(map[string]interface{}) + if !ok { + *err = errors.New(cmn.Fmt("Expected map but got type %v", reflect.TypeOf(o))) + return + } + // TODO: ensure that all fields are set? + // TODO: disallow unknown oMap fields? + for _, fieldInfo := range typeInfo.Fields { + f := fieldInfo + fieldIdx, fieldType, opts := f.Index, f.Type, f.Options + value, ok := oMap[opts.JSONName] + if !ok { + continue // Skip missing fields. + } + fieldRv := rv.Field(fieldIdx) + readReflectJSON(fieldRv, fieldType, opts, value, err) + } + } + } + + case reflect.String: + str, ok := o.(string) + if !ok { + *err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o))) + return + } + //log.Info("Read string", "str", str) + rv.SetString(str) + + case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: + num, ok := o.(float64) + if !ok { + *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) + return + } + //log.Info("Read num", "num", num) + rv.SetInt(int64(num)) + + case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint: + num, ok := o.(float64) + if !ok { + *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) + return + } + if num < 0 { + *err = errors.New(cmn.Fmt("Expected unsigned numeric but got %v", num)) + return + } + //log.Info("Read num", "num", num) + rv.SetUint(uint64(num)) + + case reflect.Float64, reflect.Float32: + if !opts.Unsafe { + *err = errors.New("Wire float* support requires `wire:\"unsafe\"`") + return + } + num, ok := o.(float64) + if !ok { + *err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o))) + return + } + //log.Info("Read num", "num", num) + rv.SetFloat(num) + + case reflect.Bool: + bl, ok := o.(bool) + if !ok { + *err = errors.New(cmn.Fmt("Expected boolean but got type %v", reflect.TypeOf(o))) + return + } + //log.Info("Read boolean", "boolean", bl) + rv.SetBool(bl) + + default: + cmn.PanicSanity(cmn.Fmt("Unknown field type %v", rt.Kind())) + } +}