summaryrefslogtreecommitdiff
path: root/data/abi/abi_json.go
diff options
context:
space:
mode:
Diffstat (limited to 'data/abi/abi_json.go')
-rw-r--r--data/abi/abi_json.go254
1 files changed, 254 insertions, 0 deletions
diff --git a/data/abi/abi_json.go b/data/abi/abi_json.go
new file mode 100644
index 000000000..482419e6b
--- /dev/null
+++ b/data/abi/abi_json.go
@@ -0,0 +1,254 @@
+// Copyright (C) 2019-2021 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
+
+package abi
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "github.com/algorand/go-algorand/data/basics"
+ "math/big"
+)
+
+func castBigIntToNearestPrimitive(num *big.Int, bitSize uint16) (interface{}, error) {
+ if num.BitLen() > int(bitSize) {
+ return nil, fmt.Errorf("cast big int to nearest primitive failure: %v >= 2^%d", num, bitSize)
+ } else if num.Sign() < 0 {
+ return nil, fmt.Errorf("cannot cast big int to near primitive: %v < 0", num)
+ }
+
+ switch bitSize / 8 {
+ case 1:
+ return uint8(num.Uint64()), nil
+ case 2:
+ return uint16(num.Uint64()), nil
+ case 3, 4:
+ return uint32(num.Uint64()), nil
+ case 5, 6, 7, 8:
+ return num.Uint64(), nil
+ default:
+ return num, nil
+ }
+}
+
+// MarshalToJSON convert golang value to JSON format from ABI type
+func (t Type) MarshalToJSON(value interface{}) ([]byte, error) {
+ switch t.abiTypeID {
+ case Uint:
+ bytesUint, err := encodeInt(value, t.bitSize)
+ if err != nil {
+ return nil, err
+ }
+ return new(big.Int).SetBytes(bytesUint).MarshalJSON()
+ case Ufixed:
+ denom := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(t.precision)), nil)
+ encodedUint, err := encodeInt(value, t.bitSize)
+ if err != nil {
+ return nil, err
+ }
+ return []byte(new(big.Rat).SetFrac(new(big.Int).SetBytes(encodedUint), denom).FloatString(int(t.precision))), nil
+ case Bool:
+ boolValue, ok := value.(bool)
+ if !ok {
+ return nil, fmt.Errorf("cannot infer to bool for marshal to JSON")
+ }
+ return json.Marshal(boolValue)
+ case Byte:
+ byteValue, ok := value.(byte)
+ if !ok {
+ return nil, fmt.Errorf("cannot infer to byte for marshal to JSON")
+ }
+ return json.Marshal(byteValue)
+ case Address:
+ var addressInternal basics.Address
+ switch valueCasted := value.(type) {
+ case []byte:
+ copy(addressInternal[:], valueCasted[:])
+ return json.Marshal(addressInternal.String())
+ case [addressByteSize]byte:
+ addressInternal = valueCasted
+ return json.Marshal(addressInternal.String())
+ default:
+ return nil, fmt.Errorf("cannot infer to byte slice/array for marshal to JSON")
+ }
+ case ArrayStatic, ArrayDynamic:
+ values, err := inferToSlice(value)
+ if err != nil {
+ return nil, err
+ }
+ if t.abiTypeID == ArrayStatic && int(t.staticLength) != len(values) {
+ return nil, fmt.Errorf("length of slice %d != type specific length %d", len(values), t.staticLength)
+ }
+ if t.childTypes[0].abiTypeID == Byte {
+ byteArr := make([]byte, len(values))
+ for i := 0; i < len(values); i++ {
+ tempByte, ok := values[i].(byte)
+ if !ok {
+ return nil, fmt.Errorf("cannot infer byte element from slice")
+ }
+ byteArr[i] = tempByte
+ }
+ return json.Marshal(byteArr)
+ }
+ rawMsgSlice := make([]json.RawMessage, len(values))
+ for i := 0; i < len(values); i++ {
+ rawMsgSlice[i], err = t.childTypes[0].MarshalToJSON(values[i])
+ if err != nil {
+ return nil, err
+ }
+ }
+ return json.Marshal(rawMsgSlice)
+ case String:
+ stringVal, ok := value.(string)
+ if !ok {
+ return nil, fmt.Errorf("cannot infer to string for marshal to JSON")
+ }
+ return json.Marshal(stringVal)
+ case Tuple:
+ values, err := inferToSlice(value)
+ if err != nil {
+ return nil, err
+ }
+ if len(values) != int(t.staticLength) {
+ return nil, fmt.Errorf("tuple element number != value slice length")
+ }
+ rawMsgSlice := make([]json.RawMessage, len(values))
+ for i := 0; i < len(values); i++ {
+ rawMsgSlice[i], err = t.childTypes[i].MarshalToJSON(values[i])
+ if err != nil {
+ return nil, err
+ }
+ }
+ return json.Marshal(rawMsgSlice)
+ default:
+ return nil, fmt.Errorf("cannot infer ABI type for marshalling value to JSON")
+ }
+}
+
+// UnmarshalFromJSON convert bytes to golang value following ABI type and encoding rules
+func (t Type) UnmarshalFromJSON(jsonEncoded []byte) (interface{}, error) {
+ switch t.abiTypeID {
+ case Uint:
+ num := new(big.Int)
+ if err := num.UnmarshalJSON(jsonEncoded); err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to uint: %v", string(jsonEncoded), err)
+ }
+ return castBigIntToNearestPrimitive(num, t.bitSize)
+ case Ufixed:
+ floatTemp := new(big.Rat)
+ if err := floatTemp.UnmarshalText(jsonEncoded); err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to ufixed: %v", string(jsonEncoded), err)
+ }
+ denom := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(t.precision)), nil)
+ denomRat := new(big.Rat).SetInt(denom)
+ numeratorRat := new(big.Rat).Mul(denomRat, floatTemp)
+ if !numeratorRat.IsInt() {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to ufixed: precision out of range", string(jsonEncoded))
+ }
+ return castBigIntToNearestPrimitive(numeratorRat.Num(), t.bitSize)
+ case Bool:
+ var elem bool
+ if err := json.Unmarshal(jsonEncoded, &elem); err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to bool: %v", string(jsonEncoded), err)
+ }
+ return elem, nil
+ case Byte:
+ var elem byte
+ if err := json.Unmarshal(jsonEncoded, &elem); err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded to byte: %v", err)
+ }
+ return elem, nil
+ case Address:
+ var addrStr string
+ if err := json.Unmarshal(jsonEncoded, &addrStr); err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded to string: %v", err)
+ }
+ addr, err := basics.UnmarshalChecksumAddress(addrStr)
+ if err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to address: %v", string(jsonEncoded), err)
+ }
+ return addr[:], nil
+ case ArrayStatic, ArrayDynamic:
+ if t.childTypes[0].abiTypeID == Byte && bytes.HasPrefix(jsonEncoded, []byte{'"'}) {
+ var byteArr []byte
+ err := json.Unmarshal(jsonEncoded, &byteArr)
+ if err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to bytes: %v", string(jsonEncoded), err)
+ }
+ if t.abiTypeID == ArrayStatic && len(byteArr) != int(t.staticLength) {
+ return nil, fmt.Errorf("length of slice %d != type specific length %d", len(byteArr), t.staticLength)
+ }
+ outInterface := make([]interface{}, len(byteArr))
+ for i := 0; i < len(byteArr); i++ {
+ outInterface[i] = byteArr[i]
+ }
+ return outInterface, nil
+ }
+ var elems []json.RawMessage
+ if err := json.Unmarshal(jsonEncoded, &elems); err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to array: %v", string(jsonEncoded), err)
+ }
+ if t.abiTypeID == ArrayStatic && len(elems) != int(t.staticLength) {
+ return nil, fmt.Errorf("JSON array element number != ABI array elem number")
+ }
+ values := make([]interface{}, len(elems))
+ for i := 0; i < len(elems); i++ {
+ tempValue, err := t.childTypes[0].UnmarshalFromJSON(elems[i])
+ if err != nil {
+ return nil, err
+ }
+ values[i] = tempValue
+ }
+ return values, nil
+ case String:
+ stringEncoded := string(jsonEncoded)
+ if bytes.HasPrefix(jsonEncoded, []byte{'"'}) {
+ var stringVar string
+ if err := json.Unmarshal(jsonEncoded, &stringVar); err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to string: %v", stringEncoded, err)
+ }
+ return stringVar, nil
+ } else if bytes.HasPrefix(jsonEncoded, []byte{'['}) {
+ var elems []byte
+ if err := json.Unmarshal(jsonEncoded, &elems); err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to string: %v", stringEncoded, err)
+ }
+ return string(elems), nil
+ } else {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to string", stringEncoded)
+ }
+ case Tuple:
+ var elems []json.RawMessage
+ if err := json.Unmarshal(jsonEncoded, &elems); err != nil {
+ return nil, fmt.Errorf("cannot cast JSON encoded (%s) to array for tuple: %v", string(jsonEncoded), err)
+ }
+ if len(elems) != int(t.staticLength) {
+ return nil, fmt.Errorf("JSON array element number != ABI tuple elem number")
+ }
+ values := make([]interface{}, len(elems))
+ for i := 0; i < len(elems); i++ {
+ tempValue, err := t.childTypes[i].UnmarshalFromJSON(elems[i])
+ if err != nil {
+ return nil, err
+ }
+ values[i] = tempValue
+ }
+ return values, nil
+ default:
+ return nil, fmt.Errorf("cannot cast JSON encoded %s to ABI encoding stuff", string(jsonEncoded))
+ }
+}