github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/amino/json_decode.go (about)

     1  package amino
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"reflect"
     8  	"strings"
     9  
    10  	"github.com/gnolang/gno/tm2/pkg/errors"
    11  )
    12  
    13  // ----------------------------------------
    14  // cdc.decodeReflectJSON
    15  
    16  // CONTRACT: rv.CanAddr() is true.
    17  func (cdc *Codec) decodeReflectJSON(bz []byte, info *TypeInfo, rv reflect.Value, fopts FieldOptions) (err error) {
    18  	if !rv.CanAddr() {
    19  		panic("rv not addressable")
    20  	}
    21  	if info.Type.Kind() == reflect.Interface && rv.Kind() == reflect.Ptr {
    22  		panic("should not happen")
    23  	}
    24  	if printLog {
    25  		fmt.Printf("(D) decodeReflectJSON(bz: %s, info: %v, rv: %#v (%v), fopts: %v)\n",
    26  			bz, info, rv.Interface(), rv.Type(), fopts)
    27  		defer func() {
    28  			fmt.Printf("(D) -> err: %v\n", err)
    29  		}()
    30  	}
    31  
    32  	// Special case for null for either interface, pointer, slice
    33  	// NOTE: This doesn't match the binary implementation completely.
    34  	if nullBytes(bz) {
    35  		rv.Set(defaultValue(rv.Type()))
    36  		return
    37  	}
    38  
    39  	// Dereference-and-construct if pointer.
    40  	rv = maybeDerefAndConstruct(rv)
    41  
    42  	// Handle the most special case, "well known".
    43  	if info.ConcreteInfo.IsJSONWellKnownType {
    44  		var ok bool
    45  		ok, err = decodeReflectJSONWellKnown(bz, info, rv, fopts)
    46  		if ok || err != nil {
    47  			return
    48  		}
    49  	}
    50  
    51  	// Handle override if a pointer to rv implements UnmarshalAmino.
    52  	if info.IsAminoMarshaler {
    53  		// First, decode repr instance from bytes.
    54  		rrv := reflect.New(info.ReprType.Type).Elem()
    55  		rinfo := info.ReprType
    56  		err = cdc.decodeReflectJSON(bz, rinfo, rrv, fopts)
    57  		if err != nil {
    58  			return
    59  		}
    60  		// Then, decode from repr instance.
    61  		uwrm := rv.Addr().MethodByName("UnmarshalAmino")
    62  		uwouts := uwrm.Call([]reflect.Value{rrv})
    63  		erri := uwouts[0].Interface()
    64  		if erri != nil {
    65  			err = erri.(error)
    66  		}
    67  		return
    68  	}
    69  
    70  	switch ikind := info.Type.Kind(); ikind {
    71  	// ----------------------------------------
    72  	// Complex
    73  
    74  	case reflect.Interface:
    75  		err = cdc.decodeReflectJSONInterface(bz, info, rv, fopts)
    76  
    77  	case reflect.Array:
    78  		err = cdc.decodeReflectJSONArray(bz, info, rv, fopts)
    79  
    80  	case reflect.Slice:
    81  		err = cdc.decodeReflectJSONSlice(bz, info, rv, fopts)
    82  
    83  	case reflect.Struct:
    84  		err = cdc.decodeReflectJSONStruct(bz, info, rv, fopts)
    85  
    86  	// ----------------------------------------
    87  	// Signed, Unsigned
    88  
    89  	case reflect.Int64, reflect.Int:
    90  		fallthrough
    91  	case reflect.Uint64, reflect.Uint:
    92  		if bz[0] != '"' || bz[len(bz)-1] != '"' {
    93  			err = errors.New(
    94  				"invalid character -- Amino:JSON int/int64/uint/uint64 expects quoted values for javascript numeric support, got: %v", //nolint: lll
    95  				string(bz),
    96  			)
    97  			if err != nil {
    98  				return
    99  			}
   100  		}
   101  		bz = bz[1 : len(bz)-1]
   102  		fallthrough
   103  	case reflect.Int32, reflect.Int16, reflect.Int8,
   104  		reflect.Uint32, reflect.Uint16, reflect.Uint8:
   105  		err = invokeStdlibJSONUnmarshal(bz, rv, fopts)
   106  
   107  	// ----------------------------------------
   108  	// Misc
   109  
   110  	case reflect.Float32, reflect.Float64:
   111  		if !fopts.Unsafe {
   112  			return errors.New("amino:JSON float* support requires `amino:\"unsafe\"`")
   113  		}
   114  		fallthrough
   115  	case reflect.Bool, reflect.String:
   116  		err = invokeStdlibJSONUnmarshal(bz, rv, fopts)
   117  
   118  	// ----------------------------------------
   119  	// Default
   120  
   121  	default:
   122  		panic(fmt.Sprintf("unsupported type %v", info.Type.Kind()))
   123  	}
   124  
   125  	return err
   126  }
   127  
   128  func invokeStdlibJSONUnmarshal(bz []byte, rv reflect.Value, fopts FieldOptions) error {
   129  	if !rv.CanAddr() && rv.Kind() != reflect.Ptr {
   130  		panic("rv not addressable nor pointer")
   131  	}
   132  
   133  	rrv := rv
   134  	if rv.Kind() != reflect.Ptr {
   135  		rrv = reflect.New(rv.Type())
   136  	}
   137  
   138  	if err := json.Unmarshal(bz, rrv.Interface()); err != nil {
   139  		return err
   140  	}
   141  	rv.Set(rrv.Elem())
   142  	return nil
   143  }
   144  
   145  // CONTRACT: rv.CanAddr() is true.
   146  func (cdc *Codec) decodeReflectJSONInterface(bz []byte, iinfo *TypeInfo, rv reflect.Value,
   147  	fopts FieldOptions,
   148  ) (err error) {
   149  	if !rv.CanAddr() {
   150  		panic("rv not addressable")
   151  	}
   152  	if printLog {
   153  		fmt.Println("(d) decodeReflectJSONInterface")
   154  		defer func() {
   155  			fmt.Printf("(d) -> err: %v\n", err)
   156  		}()
   157  	}
   158  
   159  	/*
   160  		We don't make use of user-provided interface values because there are a
   161  		lot of edge cases.
   162  
   163  		* What if the type is mismatched?
   164  		* What if the JSON field entry is missing?
   165  		* Circular references?
   166  	*/
   167  	if !rv.IsNil() {
   168  		// We don't strictly need to set it nil, but lets keep it here for a
   169  		// while in case we forget, for defensive purposes.
   170  		rv.Set(iinfo.ZeroValue)
   171  	}
   172  
   173  	// Extract type_url.
   174  	typeURL, value, err := extractJSONTypeURL(bz)
   175  	if err != nil {
   176  		return
   177  	}
   178  
   179  	// NOTE: Unlike decodeReflectBinaryInterface, we already dealt with nil in decodeReflectJSON.
   180  
   181  	// Get concrete type info.
   182  	// NOTE: Unlike decodeReflectBinaryInterface, uses the full type_url string,
   183  	// which if generated by Amino, is the name preceded by a single slash.
   184  	var cinfo *TypeInfo
   185  	cinfo, err = cdc.getTypeInfoFromTypeURLRLock(typeURL, fopts)
   186  	if err != nil {
   187  		return
   188  	}
   189  
   190  	// Extract the value bytes.
   191  	if cinfo.IsJSONAnyValueType || (cinfo.IsAminoMarshaler && cinfo.ReprType.IsJSONAnyValueType) {
   192  		bz = value
   193  	} else {
   194  		bz, err = deriveJSONObject(bz, typeURL)
   195  		if err != nil {
   196  			return
   197  		}
   198  	}
   199  
   200  	// Construct the concrete type.
   201  	crv, irvSet := constructConcreteType(cinfo)
   202  
   203  	// Decode into the concrete type.
   204  	err = cdc.decodeReflectJSON(bz, cinfo, crv, fopts)
   205  	if err != nil {
   206  		rv.Set(irvSet) // Helps with debugging
   207  		return
   208  	}
   209  
   210  	// We need to set here, for when !PointerPreferred and the type
   211  	// is say, an array of bytes (e.g. [32]byte), then we must call
   212  	// rv.Set() *after* the value was acquired.
   213  	rv.Set(irvSet)
   214  	return err
   215  }
   216  
   217  // CONTRACT: rv.CanAddr() is true.
   218  func (cdc *Codec) decodeReflectJSONArray(bz []byte, info *TypeInfo, rv reflect.Value, fopts FieldOptions) (err error) {
   219  	if !rv.CanAddr() {
   220  		panic("rv not addressable")
   221  	}
   222  	if printLog {
   223  		fmt.Println("(d) decodeReflectJSONArray")
   224  		defer func() {
   225  			fmt.Printf("(d) -> err: %v\n", err)
   226  		}()
   227  	}
   228  	ert := info.Type.Elem()
   229  	length := info.Type.Len()
   230  
   231  	switch ert.Kind() {
   232  	case reflect.Uint8: // Special case: byte array
   233  		var buf []byte
   234  		err = json.Unmarshal(bz, &buf)
   235  		if err != nil {
   236  			return
   237  		}
   238  		if len(buf) != length {
   239  			err = fmt.Errorf("decodeReflectJSONArray: byte-length mismatch, got %v want %v",
   240  				len(buf), length)
   241  		}
   242  		reflect.Copy(rv, reflect.ValueOf(buf))
   243  		return
   244  
   245  	default: // General case.
   246  		var einfo *TypeInfo
   247  		einfo, err = cdc.getTypeInfoWLock(ert)
   248  		if err != nil {
   249  			return
   250  		}
   251  
   252  		// Read into rawSlice.
   253  		var rawSlice []json.RawMessage
   254  		if err = json.Unmarshal(bz, &rawSlice); err != nil {
   255  			return
   256  		}
   257  		if len(rawSlice) != length {
   258  			err = fmt.Errorf("decodeReflectJSONArray: length mismatch, got %v want %v", len(rawSlice), length)
   259  			return
   260  		}
   261  
   262  		// Decode each item in rawSlice.
   263  		for i := 0; i < length; i++ {
   264  			erv := rv.Index(i)
   265  			ebz := rawSlice[i]
   266  			err = cdc.decodeReflectJSON(ebz, einfo, erv, fopts)
   267  			if err != nil {
   268  				return
   269  			}
   270  		}
   271  		return
   272  	}
   273  }
   274  
   275  // CONTRACT: rv.CanAddr() is true.
   276  func (cdc *Codec) decodeReflectJSONSlice(bz []byte, info *TypeInfo, rv reflect.Value, fopts FieldOptions) (err error) {
   277  	if !rv.CanAddr() {
   278  		panic("rv not addressable")
   279  	}
   280  	if printLog {
   281  		fmt.Println("(d) decodeReflectJSONSlice")
   282  		defer func() {
   283  			fmt.Printf("(d) -> err: %v\n", err)
   284  		}()
   285  	}
   286  
   287  	ert := info.Type.Elem()
   288  
   289  	switch ert.Kind() {
   290  	case reflect.Uint8: // Special case: byte slice
   291  		err = json.Unmarshal(bz, rv.Addr().Interface())
   292  		if err != nil {
   293  			return
   294  		}
   295  		if rv.Len() == 0 {
   296  			// Special case when length is 0.
   297  			// NOTE: We prefer nil slices.
   298  			rv.Set(info.ZeroValue)
   299  		}
   300  		// else {
   301  		// NOTE: Already set via json.Unmarshal() above.
   302  		// }
   303  		return
   304  
   305  	default: // General case.
   306  		var einfo *TypeInfo
   307  		einfo, err = cdc.getTypeInfoWLock(ert)
   308  		if err != nil {
   309  			return
   310  		}
   311  
   312  		// Read into rawSlice.
   313  		var rawSlice []json.RawMessage
   314  		if err = json.Unmarshal(bz, &rawSlice); err != nil {
   315  			return
   316  		}
   317  
   318  		// Special case when length is 0.
   319  		// NOTE: We prefer nil slices.
   320  		length := len(rawSlice)
   321  		if length == 0 {
   322  			rv.Set(info.ZeroValue)
   323  			return
   324  		}
   325  
   326  		// Read into a new slice.
   327  		esrt := reflect.SliceOf(ert) // TODO could be optimized.
   328  		srv := reflect.MakeSlice(esrt, length, length)
   329  		for i := 0; i < length; i++ {
   330  			erv := srv.Index(i)
   331  			ebz := rawSlice[i]
   332  			err = cdc.decodeReflectJSON(ebz, einfo, erv, fopts)
   333  			if err != nil {
   334  				return
   335  			}
   336  		}
   337  
   338  		// TODO do we need this extra step?
   339  		rv.Set(srv)
   340  		return
   341  	}
   342  }
   343  
   344  // CONTRACT: rv.CanAddr() is true.
   345  func (cdc *Codec) decodeReflectJSONStruct(bz []byte, info *TypeInfo, rv reflect.Value, fopts FieldOptions) (err error) {
   346  	if !rv.CanAddr() {
   347  		panic("rv not addressable")
   348  	}
   349  	if printLog {
   350  		fmt.Println("(d) decodeReflectJSONStruct")
   351  		defer func() {
   352  			fmt.Printf("(d) -> err: %v\n", err)
   353  		}()
   354  	}
   355  
   356  	// Map all the fields(keys) to their blobs/bytes.
   357  	// NOTE: In decodeReflectBinaryStruct, we don't need to do this,
   358  	// since fields are encoded in order.
   359  	rawMap := make(map[string]json.RawMessage)
   360  	err = json.Unmarshal(bz, &rawMap)
   361  	if err != nil {
   362  		return
   363  	}
   364  
   365  	for _, field := range info.Fields {
   366  		// Get field rv and info.
   367  		frv := rv.Field(field.Index)
   368  		finfo := field.TypeInfo
   369  
   370  		// Get value from rawMap.
   371  		valueBytes := rawMap[field.JSONName]
   372  		if len(valueBytes) == 0 {
   373  			// TODO: Since the Go stdlib's JSON codec allows case-insensitive
   374  			// keys perhaps we need to also do case-insensitive lookups here.
   375  			// So "Vanilla" and "vanilla" would both match to the same field.
   376  			// It is actually a security flaw with encoding/json library
   377  			// - See https://github.com/golang/go/issues/14750
   378  			// but perhaps we are aiming for as much compatibility here.
   379  			// JAE: I vote we depart from encoding/json, than carry a vuln.
   380  
   381  			// Set to the zero value only if not omitempty
   382  			if !field.JSONOmitEmpty {
   383  				// Set nil/zero on frv.
   384  				frv.Set(defaultValue(frv.Type()))
   385  			}
   386  
   387  			continue
   388  		}
   389  
   390  		// Decode into field rv.
   391  		err = cdc.decodeReflectJSON(valueBytes, finfo, frv, fopts)
   392  		if err != nil {
   393  			return
   394  		}
   395  	}
   396  
   397  	return nil
   398  }
   399  
   400  // ----------------------------------------
   401  // Misc.
   402  
   403  type anyWrapper struct {
   404  	TypeURL string          `json:"@type"`
   405  	Value   json.RawMessage `json:"value"`
   406  }
   407  
   408  func extractJSONTypeURL(bz []byte) (typeURL string, value json.RawMessage, err error) {
   409  	anyw := new(anyWrapper)
   410  	err = json.Unmarshal(bz, anyw)
   411  	if err != nil {
   412  		err = fmt.Errorf("cannot parse Any JSON wrapper: %w", err)
   413  		return
   414  	}
   415  
   416  	// Get typeURL.
   417  	if anyw.TypeURL == "" {
   418  		err = errors.New("JSON encoding of interfaces require non-empty @type field")
   419  		return
   420  	}
   421  	typeURL = anyw.TypeURL
   422  	value = anyw.Value
   423  	return
   424  }
   425  
   426  func deriveJSONObject(bz []byte, typeURL string) (res []byte, err error) {
   427  	str := string(bz)
   428  	if len(bz) == 0 {
   429  		err = errors.New("expected JSON object but was empty")
   430  		return
   431  	}
   432  	if !strings.HasPrefix(str, "{") {
   433  		err = fmt.Errorf("expected JSON object but was not: %s", bz)
   434  		return
   435  	}
   436  	str = strings.TrimLeft(str, " \t\r\n")
   437  	if !strings.HasPrefix(str, "{") {
   438  		err = fmt.Errorf("expected JSON object representing Any to start with '{', but got %v", string(bz))
   439  		return
   440  	}
   441  	str = str[1:]
   442  	str = strings.TrimLeft(str, " \t\r\n")
   443  	if !strings.HasPrefix(str, `"@type"`) {
   444  		err = fmt.Errorf("expected JSON object representing Any to start with \"@type\" field, but got %v", string(bz))
   445  		return
   446  	}
   447  	str = str[7:]
   448  	str = strings.TrimLeft(str, " \t\r\n")
   449  	if !strings.HasPrefix(str, ":") {
   450  		err = fmt.Errorf("expected JSON object representing Any to start with \"@type\" field, but got %v", string(bz))
   451  		return
   452  	}
   453  	str = str[1:]
   454  	str = strings.TrimLeft(str, " \t\r\n")
   455  	if !strings.HasPrefix(str, fmt.Sprintf(`"%v"`, typeURL)) {
   456  		err = fmt.Errorf("expected JSON object representing Any to start with \"@type\":\"%v\", but got %v", typeURL, string(bz))
   457  		return
   458  	}
   459  	str = str[2+len(typeURL):]
   460  	str = strings.TrimLeft(str, ",")
   461  	return []byte("{" + str), nil
   462  }
   463  
   464  func nullBytes(b []byte) bool {
   465  	return bytes.Equal(b, []byte(`null`))
   466  }
   467  
   468  func unquoteString(in string) (out string, err error) {
   469  	err = json.Unmarshal([]byte(in), &out)
   470  	return out, err
   471  }