github.com/iotexproject/iotex-core@v1.14.1-rc1/ioctl/newcmd/contract/parse.go (about)

     1  // Copyright (c) 2022 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package contract
     7  
     8  import (
     9  	"encoding/hex"
    10  	"encoding/json"
    11  	"fmt"
    12  	"math/big"
    13  	"reflect"
    14  	"strconv"
    15  	"strings"
    16  
    17  	"github.com/ethereum/go-ethereum/accounts/abi"
    18  	"github.com/ethereum/go-ethereum/common"
    19  	"github.com/iotexproject/iotex-address/address"
    20  	"github.com/pkg/errors"
    21  
    22  	"github.com/iotexproject/iotex-core/pkg/util/addrutil"
    23  )
    24  
    25  // ErrInvalidArg indicates argument is invalid
    26  var (
    27  	ErrInvalidArg = fmt.Errorf("invalid argument")
    28  )
    29  
    30  func parseAbi(abiBytes []byte) (*abi.ABI, error) {
    31  	parsedAbi, err := abi.JSON(strings.NewReader(string(abiBytes)))
    32  	if err != nil {
    33  		return nil, errors.Wrap(err, "failed to unmarshal abi")
    34  	}
    35  	return &parsedAbi, nil
    36  }
    37  
    38  func parseInput(rowInput string) (map[string]interface{}, error) {
    39  	var input map[string]interface{}
    40  	if err := json.Unmarshal([]byte(rowInput), &input); err != nil {
    41  		return nil, errors.Wrap(err, "failed to unmarshal arguments")
    42  	}
    43  	return input, nil
    44  }
    45  
    46  func parseOutput(targetAbi *abi.ABI, targetMethod string, result string) (string, error) {
    47  	resultBytes, err := hex.DecodeString(result)
    48  	if err != nil {
    49  		return "", errors.Wrap(err, "failed to decode result")
    50  	}
    51  
    52  	var (
    53  		outputArgs = targetAbi.Methods[targetMethod].Outputs
    54  		tupleStr   = make([]string, 0, len(outputArgs))
    55  	)
    56  
    57  	v, err := targetAbi.Unpack(targetMethod, resultBytes)
    58  	if err != nil {
    59  		return "", errors.Wrap(err, "failed to parse output")
    60  	}
    61  
    62  	if len(outputArgs) == 1 {
    63  		elemStr, _ := parseOutputArgument(v[0], &outputArgs[0].Type)
    64  		return elemStr, nil
    65  	}
    66  
    67  	for i, field := range v {
    68  		elemStr, _ := parseOutputArgument(field, &outputArgs[i].Type)
    69  		tupleStr = append(tupleStr, outputArgs[i].Name+":"+elemStr)
    70  	}
    71  	return "{" + strings.Join(tupleStr, " ") + "}", nil
    72  }
    73  
    74  // parseInputArgument parses input's argument as golang variable
    75  func parseInputArgument(t *abi.Type, arg interface{}) (interface{}, error) {
    76  	switch t.T {
    77  	case abi.BoolTy:
    78  		if reflect.TypeOf(arg).Kind() != reflect.Bool {
    79  			return nil, ErrInvalidArg
    80  		}
    81  
    82  	case abi.StringTy:
    83  		if reflect.TypeOf(arg).Kind() != reflect.String {
    84  			return nil, ErrInvalidArg
    85  		}
    86  
    87  	case abi.SliceTy:
    88  		if reflect.TypeOf(arg).Kind() != reflect.Slice {
    89  			return nil, ErrInvalidArg
    90  		}
    91  
    92  		slice := reflect.MakeSlice(t.GetType(), 0, t.Size)
    93  
    94  		s := reflect.ValueOf(arg)
    95  		for i := 0; i < s.Len(); i++ {
    96  			ele, err := parseInputArgument(t.Elem, s.Index(i).Interface())
    97  			if err != nil {
    98  				return nil, err
    99  			}
   100  			slice = reflect.Append(slice, reflect.ValueOf(ele))
   101  		}
   102  
   103  		arg = slice.Interface()
   104  
   105  	case abi.ArrayTy:
   106  		if reflect.TypeOf(arg).Kind() != reflect.Slice {
   107  			return nil, ErrInvalidArg
   108  		}
   109  
   110  		arrayType := reflect.ArrayOf(t.Size, t.Elem.GetType())
   111  		array := reflect.New(arrayType).Elem()
   112  
   113  		s := reflect.ValueOf(arg)
   114  		for i := 0; i < s.Len(); i++ {
   115  			ele, err := parseInputArgument(t.Elem, s.Index(i).Interface())
   116  			if err != nil {
   117  				return nil, err
   118  			}
   119  			array.Index(i).Set(reflect.ValueOf(ele))
   120  		}
   121  
   122  		arg = array.Interface()
   123  
   124  	// support both of Ether address & IoTeX address input
   125  	case abi.AddressTy:
   126  		var err error
   127  		addrString, ok := arg.(string)
   128  		if !ok {
   129  			return nil, ErrInvalidArg
   130  		}
   131  
   132  		if common.IsHexAddress(addrString) {
   133  			arg = common.HexToAddress(addrString)
   134  		} else {
   135  			arg, err = addrutil.IoAddrToEvmAddr(addrString)
   136  			if err != nil {
   137  				return nil, err
   138  			}
   139  		}
   140  
   141  	// support both number & string input
   142  	case abi.IntTy:
   143  		var ok bool
   144  		var err error
   145  
   146  		k := reflect.TypeOf(arg).Kind()
   147  		if k != reflect.String && k != reflect.Float64 {
   148  			return nil, ErrInvalidArg
   149  		}
   150  
   151  		var value int64
   152  		switch t.Size {
   153  		case 8:
   154  			if k == reflect.String {
   155  				value, err = strconv.ParseInt(arg.(string), 10, 8)
   156  				arg = int8(value)
   157  			} else {
   158  				arg = int8(arg.(float64))
   159  			}
   160  		case 16:
   161  			if k == reflect.String {
   162  				value, err = strconv.ParseInt(arg.(string), 10, 16)
   163  				arg = int16(value)
   164  			} else {
   165  				arg = int16(arg.(float64))
   166  			}
   167  		case 32:
   168  			if k == reflect.String {
   169  				value, err = strconv.ParseInt(arg.(string), 10, 32)
   170  				arg = int32(value)
   171  			} else {
   172  				arg = int32(arg.(float64))
   173  			}
   174  		case 64:
   175  			if k == reflect.String {
   176  				arg, err = strconv.ParseInt(arg.(string), 10, 64)
   177  			} else {
   178  				arg = int64(arg.(float64))
   179  			}
   180  		default:
   181  			if k == reflect.String {
   182  				arg, ok = new(big.Int).SetString(arg.(string), 10)
   183  				if !ok {
   184  					return nil, ErrInvalidArg
   185  				}
   186  			} else {
   187  				arg = big.NewInt(int64(arg.(float64)))
   188  			}
   189  		}
   190  
   191  		if err != nil {
   192  			return nil, err
   193  		}
   194  
   195  	// support both number & string input
   196  	case abi.UintTy:
   197  		var ok bool
   198  		var err error
   199  
   200  		k := reflect.TypeOf(arg).Kind()
   201  		if k != reflect.String && k != reflect.Float64 {
   202  			return nil, ErrInvalidArg
   203  		}
   204  
   205  		var value uint64
   206  		switch t.Size {
   207  		case 8:
   208  			if k == reflect.String {
   209  				value, err = strconv.ParseUint(arg.(string), 10, 8)
   210  				arg = uint8(value)
   211  			} else {
   212  				arg = uint8(arg.(float64))
   213  			}
   214  		case 16:
   215  			if k == reflect.String {
   216  				value, err = strconv.ParseUint(arg.(string), 10, 16)
   217  				arg = uint16(value)
   218  			} else {
   219  				arg = uint16(arg.(float64))
   220  			}
   221  		case 32:
   222  			if k == reflect.String {
   223  				value, err = strconv.ParseUint(arg.(string), 10, 32)
   224  				arg = uint32(value)
   225  			} else {
   226  				arg = uint32(arg.(float64))
   227  			}
   228  		case 64:
   229  			if k == reflect.String {
   230  				arg, err = strconv.ParseUint(arg.(string), 10, 64)
   231  			} else {
   232  				arg = uint64(arg.(float64))
   233  			}
   234  		default:
   235  			if k == reflect.String {
   236  				arg, ok = new(big.Int).SetString(arg.(string), 10)
   237  				if !ok {
   238  					return nil, ErrInvalidArg
   239  				}
   240  			} else {
   241  				arg = big.NewInt(int64(arg.(float64)))
   242  			}
   243  
   244  			if arg.(*big.Int).Cmp(big.NewInt(0)) < 0 {
   245  				return nil, ErrInvalidArg
   246  			}
   247  		}
   248  
   249  		if err != nil {
   250  			return nil, err
   251  		}
   252  
   253  	case abi.BytesTy:
   254  		if reflect.TypeOf(arg).Kind() != reflect.String {
   255  			return nil, ErrInvalidArg
   256  		}
   257  
   258  		bytecode, err := decodeBytecode(arg.(string))
   259  		if err != nil {
   260  			return nil, err
   261  		}
   262  
   263  		bytes := reflect.MakeSlice(t.GetType(), 0, len(bytecode))
   264  
   265  		for _, oneByte := range bytecode {
   266  			bytes = reflect.Append(bytes, reflect.ValueOf(oneByte))
   267  		}
   268  
   269  		arg = bytes.Interface()
   270  
   271  	case abi.FixedBytesTy, abi.FunctionTy:
   272  		if reflect.TypeOf(arg).Kind() != reflect.String {
   273  			return nil, ErrInvalidArg
   274  		}
   275  
   276  		bytecode, err := decodeBytecode(arg.(string))
   277  		if err != nil {
   278  			return nil, err
   279  		}
   280  
   281  		if t.Size != len(bytecode) {
   282  			return nil, ErrInvalidArg
   283  		}
   284  
   285  		bytesType := reflect.ArrayOf(t.Size, reflect.TypeOf(uint8(0)))
   286  		bytes := reflect.New(bytesType).Elem()
   287  
   288  		for i, oneByte := range bytecode {
   289  			bytes.Index(i).Set(reflect.ValueOf(oneByte))
   290  		}
   291  
   292  		arg = bytes.Interface()
   293  
   294  	default:
   295  		return nil, ErrInvalidArg
   296  	}
   297  	return arg, nil
   298  }
   299  
   300  // parseOutputArgument parses output's argument as human-readable string
   301  func parseOutputArgument(v interface{}, t *abi.Type) (string, bool) {
   302  	str := fmt.Sprint(v)
   303  	ok := false
   304  
   305  	switch t.T {
   306  	case abi.StringTy, abi.BoolTy:
   307  		// case abi.StringTy & abi.BoolTy can be handled by fmt.Sprint()
   308  		ok = true
   309  
   310  	case abi.TupleTy:
   311  		if reflect.TypeOf(v).Kind() == reflect.Struct {
   312  			ok = true
   313  
   314  			tupleStr := make([]string, 0, len(t.TupleElems))
   315  			for i, elem := range t.TupleElems {
   316  				elemStr, elemOk := parseOutputArgument(reflect.ValueOf(v).Field(i).Interface(), elem)
   317  				tupleStr = append(tupleStr, t.TupleRawNames[i]+":"+elemStr)
   318  				ok = ok && elemOk
   319  			}
   320  
   321  			str = "{" + strings.Join(tupleStr, " ") + "}"
   322  		}
   323  
   324  	case abi.SliceTy, abi.ArrayTy:
   325  		if reflect.TypeOf(v).Kind() == reflect.Slice || reflect.TypeOf(v).Kind() == reflect.Array {
   326  			ok = true
   327  
   328  			value := reflect.ValueOf(v)
   329  			sliceStr := make([]string, 0, value.Len())
   330  			for i := 0; i < value.Len(); i++ {
   331  				elemStr, elemOk := parseOutputArgument(value.Index(i).Interface(), t.Elem)
   332  				sliceStr = append(sliceStr, elemStr)
   333  				ok = ok && elemOk
   334  			}
   335  
   336  			str = "[" + strings.Join(sliceStr, " ") + "]"
   337  		}
   338  
   339  	case abi.IntTy, abi.UintTy:
   340  		if reflect.TypeOf(v) == reflect.TypeOf(big.NewInt(0)) {
   341  			var bigInt *big.Int
   342  			bigInt, ok = v.(*big.Int)
   343  			if ok {
   344  				str = bigInt.String()
   345  			}
   346  		} else if 2 <= reflect.TypeOf(v).Kind() && reflect.TypeOf(v).Kind() <= 11 {
   347  			// other integer types (int8,uint16,...) can be handled by fmt.Sprint(v)
   348  			ok = true
   349  		}
   350  
   351  	case abi.AddressTy:
   352  		if reflect.TypeOf(v) == reflect.TypeOf(common.Address{}) {
   353  			var ethAddr common.Address
   354  			ethAddr, ok = v.(common.Address)
   355  			if ok {
   356  				ioAddress, err := address.FromBytes(ethAddr.Bytes())
   357  				if err == nil {
   358  					str = ioAddress.String()
   359  				}
   360  			}
   361  		}
   362  
   363  	case abi.BytesTy:
   364  		if reflect.TypeOf(v) == reflect.TypeOf([]byte{}) {
   365  			var bytes []byte
   366  			bytes, ok = v.([]byte)
   367  			if ok {
   368  				str = "0x" + hex.EncodeToString(bytes)
   369  			}
   370  		}
   371  
   372  	case abi.FixedBytesTy, abi.FunctionTy:
   373  		if reflect.TypeOf(v).Kind() == reflect.Array && reflect.TypeOf(v).Elem() == reflect.TypeOf(byte(0)) {
   374  			bytesValue := reflect.ValueOf(v)
   375  			byteSlice := reflect.MakeSlice(reflect.TypeOf([]byte{}), bytesValue.Len(), bytesValue.Len())
   376  			reflect.Copy(byteSlice, bytesValue)
   377  
   378  			str = "0x" + hex.EncodeToString(byteSlice.Bytes())
   379  			ok = true
   380  		}
   381  	}
   382  
   383  	return str, ok
   384  }