github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/vm/stackitem/json.go (about) 1 package stackitem 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/json" 7 "errors" 8 "fmt" 9 gio "io" 10 "math/big" 11 "strconv" 12 "strings" 13 ) 14 15 // decoder is a wrapper around json.Decoder helping to mimic C# json decoder behavior. 16 type decoder struct { 17 json.Decoder 18 19 count int 20 depth int 21 // bestIntPrecision denotes whether maximum allowed integer precision should 22 // be used to parse big.Int items. If false, then default NeoC# value will be 23 // used which doesn't allow to precisely parse big values. This behaviour is 24 // managed by the config.HFBasilisk. 25 bestIntPrecision bool 26 } 27 28 // MaxAllowedInteger is the maximum integer allowed to be encoded. 29 const MaxAllowedInteger = 2<<53 - 1 30 31 // MaxJSONDepth is the maximum allowed nesting level of an encoded/decoded JSON. 32 const MaxJSONDepth = 10 33 34 const ( 35 // MaxIntegerPrec is the maximum precision allowed for big.Integer parsing. 36 // It allows to properly parse integer numbers that our 256-bit VM is able to 37 // handle. 38 MaxIntegerPrec = 1<<8 + 1 39 // CompatIntegerPrec is the maximum precision allowed for big.Integer parsing 40 // by the C# node before the Basilisk hardfork. It doesn't allow to precisely 41 // parse big numbers, see the https://github.com/neo-project/neo/issues/2879. 42 CompatIntegerPrec = 53 43 ) 44 45 // ErrInvalidValue is returned when an item value doesn't fit some constraints 46 // during serialization or deserialization. 47 var ErrInvalidValue = errors.New("invalid value") 48 49 // ErrTooDeep is returned when JSON encoder/decoder goes beyond MaxJSONDepth in 50 // its processing. 51 var ErrTooDeep = errors.New("too deep") 52 53 // ToJSON encodes Item to JSON. 54 // It behaves as following: 55 // 56 // ByteArray -> base64 string 57 // BigInteger -> number 58 // Bool -> bool 59 // Null -> null 60 // Array, Struct -> array 61 // Map -> map with keys as UTF-8 bytes 62 func ToJSON(item Item) ([]byte, error) { 63 seen := make(map[Item]sliceNoPointer, typicalNumOfItems) 64 return toJSON(nil, seen, item) 65 } 66 67 // sliceNoPointer represents a sub-slice of a known slice. 68 // It doesn't contain any pointer and uses the same amount of memory as `[]byte`, 69 // but at the same type has additional information about the number of items in 70 // the stackitem (including the stackitem itself). 71 type sliceNoPointer struct { 72 start, end int 73 itemsCount int 74 } 75 76 func toJSON(data []byte, seen map[Item]sliceNoPointer, item Item) ([]byte, error) { 77 if len(data) > MaxSize { 78 return nil, errTooBigSize 79 } 80 81 if old, ok := seen[item]; ok { 82 if len(data)+old.end-old.start > MaxSize { 83 return nil, errTooBigSize 84 } 85 return append(data, data[old.start:old.end]...), nil 86 } 87 88 start := len(data) 89 var err error 90 91 switch it := item.(type) { 92 case *Array, *Struct: 93 var items []Item 94 if a, ok := it.(*Array); ok { 95 items = a.value 96 } else { 97 items = it.(*Struct).value 98 } 99 100 data = append(data, '[') 101 for i, v := range items { 102 data, err = toJSON(data, seen, v) 103 if err != nil { 104 return nil, err 105 } 106 if i < len(items)-1 { 107 data = append(data, ',') 108 } 109 } 110 data = append(data, ']') 111 seen[item] = sliceNoPointer{start: start, end: len(data)} 112 case *Map: 113 data = append(data, '{') 114 for i := range it.value { 115 // map key can always be converted to []byte 116 // but are not always a valid UTF-8. 117 raw, err := itemToJSONString(it.value[i].Key) 118 if err != nil { 119 return nil, err 120 } 121 data = append(data, raw...) 122 data = append(data, ':') 123 data, err = toJSON(data, seen, it.value[i].Value) 124 if err != nil { 125 return nil, err 126 } 127 if i < len(it.value)-1 { 128 data = append(data, ',') 129 } 130 } 131 data = append(data, '}') 132 seen[item] = sliceNoPointer{start: start, end: len(data)} 133 case *BigInteger: 134 if it.Big().CmpAbs(big.NewInt(MaxAllowedInteger)) == 1 { 135 return nil, fmt.Errorf("%w (MaxAllowedInteger)", ErrInvalidValue) 136 } 137 data = append(data, it.Big().String()...) 138 case *ByteArray, *Buffer: 139 raw, err := itemToJSONString(it) 140 if err != nil { 141 return nil, err 142 } 143 data = append(data, raw...) 144 case Bool: 145 if it { 146 data = append(data, "true"...) 147 } else { 148 data = append(data, "false"...) 149 } 150 case Null: 151 data = append(data, "null"...) 152 default: 153 return nil, fmt.Errorf("%w: %s", ErrUnserializable, it.String()) 154 } 155 if len(data) > MaxSize { 156 return nil, errTooBigSize 157 } 158 return data, nil 159 } 160 161 // itemToJSONString converts it to a string 162 // in quotation marks with control characters escaped. 163 func itemToJSONString(it Item) ([]byte, error) { 164 s, err := ToString(it) 165 if err != nil { 166 return nil, err 167 } 168 data, _ := json.Marshal(s) // error never occurs because `ToString` checks for validity 169 170 // ref https://github.com/neo-project/neo-modules/issues/375 and https://github.com/dotnet/runtime/issues/35281 171 return bytes.Replace(data, []byte{'+'}, []byte("\\u002B"), -1), nil 172 } 173 174 // FromJSON decodes an Item from JSON. 175 // It behaves as following: 176 // 177 // string -> ByteArray from base64 178 // number -> BigInteger 179 // bool -> Bool 180 // null -> Null 181 // array -> Array 182 // map -> Map, keys are UTF-8 183 func FromJSON(data []byte, maxCount int, bestIntPrecision bool) (Item, error) { 184 d := decoder{ 185 Decoder: *json.NewDecoder(bytes.NewReader(data)), 186 count: maxCount, 187 bestIntPrecision: bestIntPrecision, 188 } 189 d.UseNumber() 190 item, err := d.decode() 191 if err != nil { 192 return nil, err 193 } 194 _, err = d.Token() 195 if !errors.Is(err, gio.EOF) { 196 return nil, fmt.Errorf("%w: unexpected items", ErrInvalidValue) 197 } 198 return item, nil 199 } 200 201 func (d *decoder) decode() (Item, error) { 202 tok, err := d.Token() 203 if err != nil { 204 return nil, err 205 } 206 207 d.count-- 208 if d.count < 0 && tok != json.Delim('}') && tok != json.Delim(']') { 209 return nil, errTooBigElements 210 } 211 212 switch t := tok.(type) { 213 case json.Delim: 214 switch t { 215 case json.Delim('{'), json.Delim('['): 216 if d.depth == MaxJSONDepth { 217 return nil, ErrTooDeep 218 } 219 d.depth++ 220 var item Item 221 if t == json.Delim('{') { 222 item, err = d.decodeMap() 223 } else { 224 item, err = d.decodeArray() 225 } 226 d.depth-- 227 return item, err 228 default: 229 d.count++ 230 // no error above means corresponding closing token 231 // was encountered for map or array respectively 232 return nil, nil 233 } 234 case string: 235 return NewByteArray([]byte(t)), nil 236 case json.Number: 237 ts := t.String() 238 var ( 239 num *big.Int 240 ok bool 241 ) 242 isScientific := strings.Contains(ts, "e+") || strings.Contains(ts, "E+") 243 if isScientific { 244 // As a special case numbers like 2.8e+22 are allowed (SetString rejects them). 245 // That's the way how C# code works. 246 var prec uint = CompatIntegerPrec 247 if d.bestIntPrecision { 248 prec = MaxIntegerPrec 249 } 250 f, _, err := big.ParseFloat(ts, 10, prec, big.ToNearestEven) 251 if err != nil { 252 return nil, fmt.Errorf("%w (malformed exp value for int)", ErrInvalidValue) 253 } 254 num = new(big.Int) 255 _, acc := f.Int(num) 256 ok = acc == big.Exact 257 } else { 258 dot := strings.IndexByte(ts, '.') 259 if dot != -1 { 260 // As a special case numbers like 123.000 are allowed (SetString rejects them). 261 // And yes, that's the way C# code works also. 262 for _, r := range ts[dot+1:] { 263 if r != '0' { 264 return nil, fmt.Errorf("%w (real value for int)", ErrInvalidValue) 265 } 266 } 267 ts = ts[:dot] 268 } 269 num, ok = new(big.Int).SetString(ts, 10) 270 } 271 if !ok { 272 return nil, fmt.Errorf("%w (integer)", ErrInvalidValue) 273 } 274 return NewBigInteger(num), nil 275 case bool: 276 return NewBool(t), nil 277 default: 278 // it can be only `nil` 279 return Null{}, nil 280 } 281 } 282 283 func (d *decoder) decodeArray() (*Array, error) { 284 items := []Item{} 285 for { 286 item, err := d.decode() 287 if err != nil { 288 return nil, err 289 } 290 if item == nil { 291 return NewArray(items), nil 292 } 293 items = append(items, item) 294 } 295 } 296 297 func (d *decoder) decodeMap() (*Map, error) { 298 m := NewMap() 299 for { 300 key, err := d.Token() 301 if err != nil { 302 return nil, err 303 } 304 k, ok := key.(string) 305 if !ok { 306 return m, nil 307 } 308 309 d.count-- 310 if d.count < 0 { 311 return nil, errTooBigElements 312 } 313 val, err := d.decode() 314 if err != nil { 315 return nil, err 316 } 317 m.Add(NewByteArray([]byte(k)), val) 318 } 319 } 320 321 // ToJSONWithTypes serializes any stackitem to JSON in a lossless way. 322 func ToJSONWithTypes(item Item) ([]byte, error) { 323 return toJSONWithTypes(nil, item, make(map[Item]sliceNoPointer, typicalNumOfItems)) 324 } 325 326 func toJSONWithTypes(data []byte, item Item, seen map[Item]sliceNoPointer) ([]byte, error) { 327 if item == nil { 328 return nil, fmt.Errorf("%w: nil", ErrUnserializable) 329 } 330 if old, ok := seen[item]; ok { 331 if old.end == 0 { 332 // Compound item marshaling which has not yet finished. 333 return nil, ErrRecursive 334 } 335 if len(data)+old.end-old.start > MaxSize { 336 return nil, errTooBigSize 337 } 338 return append(data, data[old.start:old.end]...), nil 339 } 340 341 var val string 342 var hasValue bool 343 switch item.(type) { 344 case Null: 345 val = `{"type":"Any"}` 346 case *Interop: 347 val = `{"type":"InteropInterface"}` 348 default: 349 val = `{"type":"` + item.Type().String() + `","value":` 350 hasValue = true 351 } 352 353 if len(data)+len(val) > MaxSize { 354 return nil, errTooBigSize 355 } 356 357 start := len(data) 358 359 data = append(data, val...) 360 if !hasValue { 361 return data, nil 362 } 363 364 // Primitive stack items are appended after the switch 365 // to reduce the amount of size checks. 366 var primitive string 367 var isBuffer bool 368 var err error 369 370 switch it := item.(type) { 371 case *Array, *Struct: 372 seen[item] = sliceNoPointer{} 373 data = append(data, '[') 374 for i, elem := range it.Value().([]Item) { 375 if i != 0 { 376 data = append(data, ',') 377 } 378 data, err = toJSONWithTypes(data, elem, seen) 379 if err != nil { 380 return nil, err 381 } 382 } 383 case Bool: 384 if it { 385 primitive = "true" 386 } else { 387 primitive = "false" 388 } 389 case *ByteArray: 390 primitive = `"` + base64.StdEncoding.EncodeToString(it.Value().([]byte)) + `"` 391 case *Buffer: 392 isBuffer = true 393 primitive = `"` + base64.StdEncoding.EncodeToString(it.Value().([]byte)) + `"` 394 case *BigInteger: 395 primitive = `"` + it.Big().String() + `"` 396 case *Map: 397 seen[item] = sliceNoPointer{} 398 data = append(data, '[') 399 for i := range it.value { 400 if i != 0 { 401 data = append(data, ',') 402 } 403 data = append(data, `{"key":`...) 404 data, err = toJSONWithTypes(data, it.value[i].Key, seen) 405 if err != nil { 406 return nil, err 407 } 408 data = append(data, `,"value":`...) 409 data, err = toJSONWithTypes(data, it.value[i].Value, seen) 410 if err != nil { 411 return nil, err 412 } 413 data = append(data, '}') 414 } 415 case *Pointer: 416 primitive = strconv.Itoa(it.pos) 417 } 418 if len(primitive) != 0 { 419 if len(data)+len(primitive)+1 > MaxSize { 420 return nil, errTooBigSize 421 } 422 data = append(data, primitive...) 423 data = append(data, '}') 424 425 if isBuffer { 426 seen[item] = sliceNoPointer{start: start, end: len(data)} 427 } 428 } else { 429 if len(data)+2 > MaxSize { // also take care of '}' 430 return nil, errTooBigSize 431 } 432 data = append(data, ']', '}') 433 434 seen[item] = sliceNoPointer{start: start, end: len(data)} 435 } 436 return data, nil 437 } 438 439 type ( 440 rawItem struct { 441 Type string `json:"type"` 442 Value json.RawMessage `json:"value,omitempty"` 443 } 444 445 rawMapElement struct { 446 Key json.RawMessage `json:"key"` 447 Value json.RawMessage `json:"value"` 448 } 449 ) 450 451 func mkErrValue(err error) error { 452 return fmt.Errorf("%w: %w", ErrInvalidValue, err) 453 } 454 455 // FromJSONWithTypes deserializes an item from typed-json representation. 456 func FromJSONWithTypes(data []byte) (Item, error) { 457 raw := new(rawItem) 458 if err := json.Unmarshal(data, raw); err != nil { 459 return nil, err 460 } 461 typ, err := FromString(raw.Type) 462 if err != nil { 463 return nil, fmt.Errorf("%w: %v", ErrInvalidType, raw.Type) 464 } 465 switch typ { 466 case AnyT: 467 return Null{}, nil 468 case PointerT: 469 var pos int 470 if err := json.Unmarshal(raw.Value, &pos); err != nil { 471 return nil, mkErrValue(err) 472 } 473 return NewPointer(pos, nil), nil 474 case BooleanT: 475 var b bool 476 if err := json.Unmarshal(raw.Value, &b); err != nil { 477 return nil, mkErrValue(err) 478 } 479 return NewBool(b), nil 480 case IntegerT: 481 var s string 482 if err := json.Unmarshal(raw.Value, &s); err != nil { 483 return nil, mkErrValue(err) 484 } 485 val, ok := new(big.Int).SetString(s, 10) 486 if !ok { 487 return nil, mkErrValue(errors.New("not an integer")) 488 } 489 return NewBigInteger(val), nil 490 case ByteArrayT, BufferT: 491 var s string 492 if err := json.Unmarshal(raw.Value, &s); err != nil { 493 return nil, mkErrValue(err) 494 } 495 val, err := base64.StdEncoding.DecodeString(s) 496 if err != nil { 497 return nil, mkErrValue(err) 498 } 499 if typ == ByteArrayT { 500 return NewByteArray(val), nil 501 } 502 return NewBuffer(val), nil 503 case ArrayT, StructT: 504 var arr []json.RawMessage 505 if err := json.Unmarshal(raw.Value, &arr); err != nil { 506 return nil, mkErrValue(err) 507 } 508 items := make([]Item, len(arr)) 509 for i := range arr { 510 it, err := FromJSONWithTypes(arr[i]) 511 if err != nil { 512 return nil, err 513 } 514 items[i] = it 515 } 516 if typ == ArrayT { 517 return NewArray(items), nil 518 } 519 return NewStruct(items), nil 520 case MapT: 521 var arr []rawMapElement 522 if err := json.Unmarshal(raw.Value, &arr); err != nil { 523 return nil, mkErrValue(err) 524 } 525 m := NewMap() 526 for i := range arr { 527 key, err := FromJSONWithTypes(arr[i].Key) 528 if err != nil { 529 return nil, err 530 } else if err = IsValidMapKey(key); err != nil { 531 return nil, err 532 } 533 value, err := FromJSONWithTypes(arr[i].Value) 534 if err != nil { 535 return nil, err 536 } 537 m.Add(key, value) 538 } 539 return m, nil 540 case InteropT: 541 return NewInterop(nil), nil 542 default: 543 return nil, fmt.Errorf("%w: %v", ErrInvalidType, typ) 544 } 545 }